Add Linux TAP
This commit is contained in:
Regular → Executable
+30
-4
@@ -77,15 +77,41 @@ Desktop.ini
|
|||||||
*.suo # Visual Studio Solution User Options
|
*.suo # Visual Studio Solution User Options
|
||||||
*.sln.docstates # Visual Studio
|
*.sln.docstates # Visual Studio
|
||||||
|
|
||||||
# Игнорируем весь lwip
|
# components/lwip is a complete local ESP-IDF component override and must be
|
||||||
|
# kept in source archives/repositories. Ignoring it would make a fresh checkout
|
||||||
|
# impossible to build.
|
||||||
|
|
||||||
|
# Local IDE configuration
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
# Windows download metadata
|
||||||
|
*:Zone.Identifier
|
||||||
|
|
||||||
|
# Keep only the modified lwIP source file
|
||||||
|
components/lwip/**
|
||||||
|
|
||||||
|
!components/lwip/
|
||||||
|
!components/lwip/lwip/
|
||||||
|
!components/lwip/lwip/src/
|
||||||
|
!components/lwip/lwip/src/core/
|
||||||
|
!components/lwip/lwip/src/core/ipv4/
|
||||||
|
!components/lwip/lwip/src/core/ipv4/etharp.c
|
||||||
|
# Keep only the modified lwIP source file
|
||||||
|
components/lwip/**
|
||||||
|
|
||||||
|
!components/lwip/
|
||||||
|
!components/lwip/lwip/
|
||||||
|
!components/lwip/lwip/src/
|
||||||
|
!components/lwip/lwip/src/core/
|
||||||
|
!components/lwip/lwip/src/core/ipv4/
|
||||||
|
!components/lwip/lwip/src/core/ipv4/etharp.c
|
||||||
|
|
||||||
|
# Keep only the modified lwIP source file
|
||||||
components/lwip/**
|
components/lwip/**
|
||||||
|
|
||||||
# Но разрешаем нужные папки
|
|
||||||
!components/lwip/
|
!components/lwip/
|
||||||
!components/lwip/lwip/
|
!components/lwip/lwip/
|
||||||
!components/lwip/lwip/src/
|
!components/lwip/lwip/src/
|
||||||
!components/lwip/lwip/src/core/
|
!components/lwip/lwip/src/core/
|
||||||
!components/lwip/lwip/src/core/ipv4/
|
!components/lwip/lwip/src/core/ipv4/
|
||||||
|
|
||||||
# И сам файл
|
|
||||||
!components/lwip/lwip/src/core/ipv4/etharp.c
|
!components/lwip/lwip/src/core/ipv4/etharp.c
|
||||||
Vendored
-19
@@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"name": "ESP-IDF",
|
|
||||||
"compilerPath": "C:\\Espressif\\tools\\xtensa-esp-elf\\esp-15.2.0_20251204\\xtensa-esp-elf\\bin\\xtensa-esp32-elf-gcc.exe",
|
|
||||||
"compileCommands": [
|
|
||||||
"${config:idf.buildPath}/compile_commands.json"
|
|
||||||
],
|
|
||||||
"intelliSenseMode": "gcc-x86",
|
|
||||||
"browse": {
|
|
||||||
"path": [
|
|
||||||
"${workspaceFolder}"
|
|
||||||
],
|
|
||||||
"limitSymbolsToIncludedHeaders": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"version": 4
|
|
||||||
}
|
|
||||||
Vendored
-10
@@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"type": "gdbtarget",
|
|
||||||
"request": "attach",
|
|
||||||
"name": "Eclipse CDT GDB Adapter"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
Vendored
-17
@@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
"C_Cpp.intelliSenseEngine": "default",
|
|
||||||
"idf.extensionActivationMode": "always",
|
|
||||||
"idf.openOcdConfigs": [
|
|
||||||
"board/esp32-wrover-kit-3.3v.cfg"
|
|
||||||
],
|
|
||||||
"idf.currentSetup": "C:\\esp\\v6.0.2\\esp-idf",
|
|
||||||
"idf.customExtraVars": {
|
|
||||||
"IDF_TARGET": "esp32"
|
|
||||||
},
|
|
||||||
"clangd.path": "C:\\Espressif\\tools\\esp-clang\\esp-20.1.1_20250829\\esp-clang\\bin\\clangd.exe",
|
|
||||||
"clangd.arguments": [
|
|
||||||
"--background-index",
|
|
||||||
"--query-driver=**",
|
|
||||||
"--compile-commands-dir=c:\\Users\\Maxpr\\monitoring\\build"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -2,5 +2,7 @@
|
|||||||
# CMakeLists in this exact order for cmake to work correctly
|
# CMakeLists in this exact order for cmake to work correctly
|
||||||
cmake_minimum_required(VERSION 3.22)
|
cmake_minimum_required(VERSION 3.22)
|
||||||
|
|
||||||
|
set(COMPONENTS main)
|
||||||
|
|
||||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
project(monitoring)
|
project(monitoring)
|
||||||
|
|||||||
@@ -1,34 +1,43 @@
|
|||||||
# ARP Monitoring for ESP32
|
# ARP Monitoring (ESP-IDF / Linux TAP)
|
||||||
|
|
||||||
> Практическая работа по анализу и модификации сетевого стека **lwIP** в среде **ESP-IDF**.
|
Практическая работа по анализу и модификации сетевого стека **lwIP** в
|
||||||
|
**ESP-IDF**. Проект перехватывает входящие ARP-пакеты, хранит обнаруженные узлы
|
||||||
|
в оперативной памяти, показывает их во встроенном веб-интерфейсе и позволяет
|
||||||
|
проверять выбранные адреса с помощью ICMP Echo (Ping).
|
||||||
|
|
||||||
## Описание
|
Рабочая конфигурация проверена на **ESP-IDF v6.0.2** с использованием целевой
|
||||||
|
платформы Linux и TAP-интерфейса без физической платы ESP32.
|
||||||
Проект представляет собой систему мониторинга устройств локальной сети на базе **ESP32**.
|
|
||||||
|
|
||||||
В ходе работы выполняется перехват входящих ARP-пакетов на уровне сетевого стека **lwIP**, извлекается информация об обнаруженных устройствах и формируется динамический список сетевых узлов. Полученные данные отображаются через встроенный HTTP-сервер.
|
|
||||||
|
|
||||||
Дополнительно реализована возможность проверки доступности устройств с помощью **ICMP Echo (Ping)**.
|
|
||||||
|
|
||||||
## Возможности
|
|
||||||
|
|
||||||
* Перехват входящих ARP-пакетов.
|
|
||||||
* Обнаружение устройств локальной сети.
|
|
||||||
* Хранение списка устройств в оперативной памяти.
|
|
||||||
* Веб-интерфейс для просмотра обнаруженных устройств.
|
|
||||||
* Проверка доступности устройств с помощью Ping.
|
|
||||||
|
|
||||||
## Структура проекта
|
## Структура проекта
|
||||||
|
|
||||||
```text
|
| Модуль | Назначение |
|
||||||
components/
|
| --- | --- |
|
||||||
│
|
| `components/lwip` | Локальная копия lwIP; в `etharp_input()` установлен перехват ARP |
|
||||||
├── arp_monitor/ Перехват ARP-пакетов
|
| `components/arp_monitor` | Принимает IP/MAC из lwIP через очередь FreeRTOS |
|
||||||
├── device_list/ Работа со списком устройств
|
| `components/device_list` | Хранит до 64 найденных устройств в RAM |
|
||||||
├── ping/ Проверка доступности устройств
|
| `components/web_server` | Отдаёт HTML-таблицу устройств и обрабатывает `/ping` |
|
||||||
├── web_server/ HTTP-сервер и веб-интерфейс
|
| `components/ping` | Запускает стандартный `esp_ping` для выбранных узлов |
|
||||||
└── lwip/ Изменённый файл etharp.c
|
| `components/protocol_examples_tapif_io` | Подключает lwIP к Linux TAP без ESP32 |
|
||||||
|
| `main/main.c` | Отвечает за инициализацию компонентов |
|
||||||
|
|
||||||
main/
|
Путь входящего пакета:
|
||||||
└── main.c Точка входа приложения
|
|
||||||
```
|
1. TAP передаёт Ethernet-кадр в lwIP.
|
||||||
|
2. `ethernet_input()` определяет тип кадра `ETHTYPE_ARP` и вызывает
|
||||||
|
`etharp_input()` из `components/lwip/lwip/src/core/ipv4/etharp.c`.
|
||||||
|
3. После разбора ARP-заголовка `etharp_input()` вызывает
|
||||||
|
`arp_monitor_report()` с IP- и MAC-адресом отправителя.
|
||||||
|
4. `arp_monitor_report()` копирует данные в неблокирующую очередь FreeRTOS.
|
||||||
|
5. Задача `arp_monitor_task` обновляет статический массив `device_list` в RAM.
|
||||||
|
6. HTTP-обработчик читает актуальный список и формирует HTML-таблицу.
|
||||||
|
|
||||||
|
## Веб-интерфейс
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Примечание
|
||||||
|
|
||||||
|
Текущая версия полностью проверена на целевой платформе Linux с использованием
|
||||||
|
TAP-интерфейса без физической платы. Для запуска на ESP32 необходимо
|
||||||
|
дополнительно реализовать инициализацию Wi-Fi и подключение устройства к
|
||||||
|
локальной сети.
|
||||||
|
|||||||
@@ -3,4 +3,5 @@ idf_component_register(
|
|||||||
INCLUDE_DIRS "include"
|
INCLUDE_DIRS "include"
|
||||||
REQUIRES
|
REQUIRES
|
||||||
device_list
|
device_list
|
||||||
|
lwip
|
||||||
)
|
)
|
||||||
@@ -20,7 +20,7 @@ void arp_monitor_init(void)
|
|||||||
arp_queue = xQueueCreate(ARP_QUEUE_LENGTH, sizeof(arp_event_t));
|
arp_queue = xQueueCreate(ARP_QUEUE_LENGTH, sizeof(arp_event_t));
|
||||||
if (arp_queue == NULL)
|
if (arp_queue == NULL)
|
||||||
{
|
{
|
||||||
ESP_LOGE(TAG, "Failde to create ARP queue");
|
ESP_LOGE(TAG, "Failed to create ARP queue");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ESP_LOGI(TAG, "ARP queue created");
|
ESP_LOGI(TAG, "ARP queue created");
|
||||||
@@ -46,6 +46,11 @@ void arp_monitor_init(void)
|
|||||||
|
|
||||||
bool arp_monitor_post_event(const arp_event_t *event)
|
bool arp_monitor_post_event(const arp_event_t *event)
|
||||||
{
|
{
|
||||||
|
if (event == NULL)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (arp_queue == NULL)
|
if (arp_queue == NULL)
|
||||||
{
|
{
|
||||||
ESP_LOGE(TAG, "ARP queue isn't initialized");
|
ESP_LOGE(TAG, "ARP queue isn't initialized");
|
||||||
@@ -61,6 +66,21 @@ bool arp_monitor_post_event(const arp_event_t *event)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void arp_monitor_report(const ip4_addr_t *ip, const struct eth_addr *mac)
|
||||||
|
{
|
||||||
|
if ((ip == NULL) || (mac == NULL))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
arp_event_t event = {
|
||||||
|
.ip = *ip
|
||||||
|
};
|
||||||
|
|
||||||
|
memcpy(event.mac, mac->addr, ETH_HWADDR_LEN);
|
||||||
|
arp_monitor_post_event(&event);
|
||||||
|
}
|
||||||
|
|
||||||
static void process_arp_event(const arp_event_t *event)
|
static void process_arp_event(const arp_event_t *event)
|
||||||
{
|
{
|
||||||
device_info_t device = {
|
device_info_t device = {
|
||||||
|
|||||||
@@ -13,5 +13,6 @@ typedef struct
|
|||||||
|
|
||||||
void arp_monitor_init(void);
|
void arp_monitor_init(void);
|
||||||
bool arp_monitor_post_event(const arp_event_t *event);
|
bool arp_monitor_post_event(const arp_event_t *event);
|
||||||
|
void arp_monitor_report(const ip4_addr_t *ip, const struct eth_addr *mac);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
idf_component_register(
|
idf_component_register(
|
||||||
SRCS "device_list.c"
|
SRCS "device_list.c"
|
||||||
INCLUDE_DIRS "include"
|
INCLUDE_DIRS "include"
|
||||||
|
REQUIRES lwip
|
||||||
)
|
)
|
||||||
@@ -34,6 +34,11 @@ device_info_t *device_list_find_by_mac(const struct eth_addr *mac)
|
|||||||
|
|
||||||
bool device_list_add_or_update(const device_info_t *device)
|
bool device_list_add_or_update(const device_info_t *device)
|
||||||
{
|
{
|
||||||
|
if (device == NULL)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
device_info_t *existing_device = device_list_find_by_mac(&device->mac);
|
device_info_t *existing_device = device_list_find_by_mac(&device->mac);
|
||||||
|
|
||||||
if (existing_device != NULL)
|
if (existing_device != NULL)
|
||||||
|
|||||||
@@ -57,17 +57,8 @@
|
|||||||
#include "netif/ethernet.h"
|
#include "netif/ethernet.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "arp_monitor.h"
|
|
||||||
|
|
||||||
static inline void arp_monitor_report(const ip4_addr_t *ip, const struct eth_addr *mac)
|
void arp_monitor_report(const ip4_addr_t *ip, const struct eth_addr *mac);
|
||||||
{
|
|
||||||
arp_event_t event;
|
|
||||||
|
|
||||||
event.ip = *ip;
|
|
||||||
memcpy(event.mac, mac->addr, ETH_HWADDR_LEN);
|
|
||||||
|
|
||||||
arp_monitor_post_event(&event);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef LWIP_HOOK_FILENAME
|
#ifdef LWIP_HOOK_FILENAME
|
||||||
#include LWIP_HOOK_FILENAME
|
#include LWIP_HOOK_FILENAME
|
||||||
|
|||||||
+14
-5
@@ -8,7 +8,6 @@
|
|||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/semphr.h"
|
#include "freertos/semphr.h"
|
||||||
|
|
||||||
static const char *TAG = "ping";
|
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
@@ -25,11 +24,11 @@ static void ping_end_callback(esp_ping_handle_t handle, void *arguments);
|
|||||||
|
|
||||||
static bool parse_mac_address(const char *text, struct eth_addr *mac)
|
static bool parse_mac_address(const char *text, struct eth_addr *mac)
|
||||||
{
|
{
|
||||||
if ((text == NULL) || (mac == NULL))
|
if ((text == NULL) || (mac == NULL) || (strlen(text) != 12))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return (sscanf(text,
|
return (sscanf(text,
|
||||||
"%2hhX:%2hhX:%2hhX:%2hhX:%2hhX:%2hhX",
|
"%2hhX%2hhX%2hhX%2hhX%2hhX%2hhX",
|
||||||
&mac->addr[0],
|
&mac->addr[0],
|
||||||
&mac->addr[1],
|
&mac->addr[1],
|
||||||
&mac->addr[2],
|
&mac->addr[2],
|
||||||
@@ -40,6 +39,9 @@ static bool parse_mac_address(const char *text, struct eth_addr *mac)
|
|||||||
|
|
||||||
void ping_selected_devices(const char *query)
|
void ping_selected_devices(const char *query)
|
||||||
{
|
{
|
||||||
|
if (query == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
const char *current = query;
|
const char *current = query;
|
||||||
|
|
||||||
while ((current = strstr(current, "mac=")) != NULL)
|
while ((current = strstr(current, "mac=")) != NULL)
|
||||||
@@ -47,7 +49,7 @@ void ping_selected_devices(const char *query)
|
|||||||
struct eth_addr mac;
|
struct eth_addr mac;
|
||||||
device_info_t *device;
|
device_info_t *device;
|
||||||
|
|
||||||
char mac_string[18];
|
char mac_string[13];
|
||||||
size_t length;
|
size_t length;
|
||||||
current += 4;
|
current += 4;
|
||||||
|
|
||||||
@@ -110,10 +112,17 @@ static void ping_device(device_info_t *device)
|
|||||||
if (esp_ping_new_session(&config, &callbacks, &ping) != ESP_OK)
|
if (esp_ping_new_session(&config, &callbacks, &ping) != ESP_OK)
|
||||||
{
|
{
|
||||||
vSemaphoreDelete(context.finished);
|
vSemaphoreDelete(context.finished);
|
||||||
|
device->active = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_ping_start(ping);
|
if (esp_ping_start(ping) != ESP_OK)
|
||||||
|
{
|
||||||
|
esp_ping_delete_session(ping);
|
||||||
|
vSemaphoreDelete(context.finished);
|
||||||
|
device->active = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
xSemaphoreTake(context.finished, portMAX_DELAY);
|
xSemaphoreTake(context.finished, portMAX_DELAY);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
idf_build_get_property(target IDF_TARGET)
|
||||||
|
|
||||||
|
if(${target} STREQUAL "linux")
|
||||||
|
idf_component_register(INCLUDE_DIRS include
|
||||||
|
SRCS linux/tapio.c linux_connect.c lwip/tapif.c
|
||||||
|
PRIV_REQUIRES esp_netif lwip)
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "This component is currently only supported for linux target")
|
||||||
|
endif()
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
menu "Example Connection Configuration"
|
||||||
|
|
||||||
|
config EXAMPLE_CONNECT_LWIP_TAPIF
|
||||||
|
bool "connect using lwip to linux tap interface"
|
||||||
|
depends on IDF_TARGET_LINUX && ESP_NETIF_TCPIP_LWIP
|
||||||
|
default y
|
||||||
|
|
||||||
|
if EXAMPLE_CONNECT_LWIP_TAPIF
|
||||||
|
config EXAMPLE_CONNECT_IPV4
|
||||||
|
bool
|
||||||
|
depends on LWIP_IPV4
|
||||||
|
default y
|
||||||
|
|
||||||
|
config EXAMPLE_CONNECT_IPV6
|
||||||
|
bool "Obtain IPv6 address"
|
||||||
|
depends on LWIP_IPV6
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
Set to true to setup link local address and wait until it's valid
|
||||||
|
|
||||||
|
config EXAMPLE_CONNECT_WAIT_FOR_IP
|
||||||
|
bool "run DHCP and wait for IP"
|
||||||
|
default y
|
||||||
|
|
||||||
|
config EXAMPLE_CONNECT_TAPIF_IP_ADDR
|
||||||
|
string "Static IP address"
|
||||||
|
default "192.168.5.100"
|
||||||
|
depends on EXAMPLE_CONNECT_IPV4 && !EXAMPLE_CONNECT_WAIT_FOR_IP
|
||||||
|
help
|
||||||
|
Set static IP address.
|
||||||
|
|
||||||
|
config EXAMPLE_CONNECT_TAPIF_NETMASK
|
||||||
|
string "Static netmask address"
|
||||||
|
default "255.255.255.0"
|
||||||
|
depends on EXAMPLE_CONNECT_IPV4 && !EXAMPLE_CONNECT_WAIT_FOR_IP
|
||||||
|
help
|
||||||
|
Set static netmask address.
|
||||||
|
|
||||||
|
config EXAMPLE_CONNECT_TAPIF_GW
|
||||||
|
string "Static gateway address"
|
||||||
|
default "192.168.5.1"
|
||||||
|
depends on EXAMPLE_CONNECT_IPV4 && !EXAMPLE_CONNECT_WAIT_FOR_IP
|
||||||
|
help
|
||||||
|
Set static gateway address.
|
||||||
|
|
||||||
|
config EXAMPLE_CONNECT_TAPIF_OUT_LOSS
|
||||||
|
int "Percentage of packets to be dropped on transmission"
|
||||||
|
default 0
|
||||||
|
range 0 100
|
||||||
|
help
|
||||||
|
Set non-zero number simulate packet loss when sending data.
|
||||||
|
Number represents probability between 0 and 100%
|
||||||
|
|
||||||
|
config EXAMPLE_CONNECT_TAPIF_IN_LOSS
|
||||||
|
int "Percentage of packets to be dropped on reception"
|
||||||
|
default 0
|
||||||
|
range 0 100
|
||||||
|
help
|
||||||
|
Set non-zero number simulate packet loss when receiving data.
|
||||||
|
Number represents probability between 0 and 100%
|
||||||
|
endif
|
||||||
|
|
||||||
|
endmenu
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
# tapif-io Component
|
||||||
|
|
||||||
|
This component implements a tap networking interface that provides connectivity to host network using `tuntap` interface in Linux.
|
||||||
|
It could be used to route lwip traffic to host side network, typically when working with the **Linux target**.
|
||||||
|
|
||||||
|
## How to use this component
|
||||||
|
|
||||||
|
### Usage of the API
|
||||||
|
|
||||||
|
1) Add the path to this component to as a dependency to the `main` component of your project using the following idf_component.yml file:
|
||||||
|
```yaml
|
||||||
|
dependencies:
|
||||||
|
tapif_io:
|
||||||
|
path: ${IDF_PATH}/examples/common_components/tapif_io
|
||||||
|
```
|
||||||
|
2) Include lwip and linux side of the configuration
|
||||||
|
```cpp
|
||||||
|
#include "esp_netif.h" // esp-netif
|
||||||
|
#include "tapio.h" // esp-netif's driver side
|
||||||
|
#include "lwip/tapif.h" // esp-netif's network stack side
|
||||||
|
```
|
||||||
|
3) Configure the esp-netif
|
||||||
|
a) setup the linux tap I/O config
|
||||||
|
```cpp
|
||||||
|
esp_netif_driver_ifconfig_t driver_cfg = {
|
||||||
|
.handle = tapio_create(),
|
||||||
|
.transmit = tapio_output,
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
b) configure the lwip netif for the tap interface
|
||||||
|
```cpp
|
||||||
|
struct esp_netif_netstack_config stack_cfg = {
|
||||||
|
.lwip = {
|
||||||
|
.init_fn = lwip_tapif_init,
|
||||||
|
.input_fn = lwip_tapif_input,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
c) configure the esp-netif basic parameters
|
||||||
|
```cpp
|
||||||
|
esp_netif_inherent_config_t base_cfg = {
|
||||||
|
.if_key = "TAP", // unique name of the interface
|
||||||
|
.flags = ESP_NETIF_FLAG_AUTOUP, // no dhcp client, starts when it's set up
|
||||||
|
.ip_info = &ip_info, // add static IP info
|
||||||
|
.route_prio = 100 // priority for setting default gateway
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
4) Initialize and attach the esp_netif to the I/O handle
|
||||||
|
```cpp
|
||||||
|
esp_netif_t *tap_netif = esp_netif_new(&cfg);
|
||||||
|
esp_netif_attach(tap_netif, driver_cfg.handle);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Host side networking
|
||||||
|
|
||||||
|
1) Create a new tun/tap interface type named `tap0`
|
||||||
|
a) You can run the script `./make_tap_netif`
|
||||||
|
b) Update the IP address of the interface to correspond to the configured static IP in previous step
|
||||||
|
|
||||||
|
2) Start the application and send/receive the packets via `tap0` interface
|
||||||
|
* it is possible to create server or client test application listening or connecting to this interface.
|
||||||
|
* it is also possible to route these packets to external network (using routing rules or simply by ip forwarding if using the same subnet)
|
||||||
|
|
||||||
|
#### Common networking/routing examples
|
||||||
|
|
||||||
|
##### Isolated internal connection
|
||||||
|
|
||||||
|
Is useful to experiment with one interface with no intention to connect to internet or external facilities.
|
||||||
|
Typically, when we want to create a server listening on the `tap0` interface and run a client in lwip, e.g. the default `tcp_client` socket example in IDF.
|
||||||
|
* Create the tap interface using `./make_tap_netif` and set the IP address **not to overlap** with any other IPv4 network range (e.g. `ip addr add 192.168.5.1/24 dev tap0`)
|
||||||
|
* Configure the `tapif_io` component to use static address from that range (e.g. `192.168.5.x`)
|
||||||
|
* Configure the `tcp_client` example to connect to the tap interface IP address (e.g. `192.168.5.1`)
|
||||||
|
* Execute a tcp server listening on the tap interface and the configured port (e.g. `nc -l 3333`)
|
||||||
|
* Build and run the `tcp_client` example to send and receive data between the server created in the previous step.
|
||||||
|
|
||||||
|
##### Connecting to the external network using IP forwarding
|
||||||
|
|
||||||
|
This allows using full-featured network facilities of your host network, but a care must be taken to the selected IP addresses to avoid potential conflicts.
|
||||||
|
* Set the IP address of the `tap0` interface from the range used by your host system's default gateway (e.g. `ip addr add 192.168.0.123/24 dev tap0`, assuming the default netif is `eth0` with IP range of `192.168.0.x` and this address doesn't overlap with any other IP address in this network)
|
||||||
|
* Configure the `tapif_io` with another address from the same range, e.g.
|
||||||
|
```text
|
||||||
|
CONFIG_EXAMPLE_CONNECT_TAPIF_IP_ADDR="192.168.0.100"
|
||||||
|
CONFIG_EXAMPLE_CONNECT_TAPIF_NETMASK="255.255.255.0"
|
||||||
|
CONFIG_EXAMPLE_CONNECT_TAPIF_GW="192.168.0.1"
|
||||||
|
```
|
||||||
|
assuming that the default gateway of your host network is configured to `192.168.0.1`
|
||||||
|
* Build and run the lwip example to interact with the host network, e.g, to send an HTTP request to a publicly available http server (if the server is reachable from your host network)
|
||||||
|
|
||||||
|
(Note, that the IP forwarding must be enabled in the host system:
|
||||||
|
```bash
|
||||||
|
echo 1 > /proc/sys/net/ipv4/ip_forward
|
||||||
|
```
|
||||||
|
)
|
||||||
|
|
||||||
|
##### Routing the internal interface to the host network with IP tables
|
||||||
|
|
||||||
|
Uses an isolated interface with routing and NAT-ing between interfaces
|
||||||
|
* Configure the `tap0` interface address **not to overlap** with any other IPv4 network range.
|
||||||
|
* Setup `MASQUERADE` target to route network traffic between `tap0` and your default network interface (`eth0` in the below example).
|
||||||
|
```bash
|
||||||
|
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
|
||||||
|
sudo iptables -A FORWARD -i eth0 -o tap0 -m state --state RELATED,ESTABLISHED -j ACCEPT
|
||||||
|
sudo iptables -A FORWARD -i tap0 -o eth0 -j ACCEPT
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Using DHCP
|
||||||
|
|
||||||
|
It's also possible to configure the lwip interface to use DHCP client (common setup for most default network interfaces, such as Ethernet or WiFi station)
|
||||||
|
and set up a DHCP server on the host machine to assign the IP address dynamically.
|
||||||
|
|
||||||
|
This component sets up a DHCP client if `CONFIG_EXAMPLE_CONNECT_WAIT_FOR_IP` is enabled and waits for assigning an IP address. See below the description of DHCP client workflow for tap interface:
|
||||||
|
|
||||||
|
1) **Configure and set the `esp-netif` up**
|
||||||
|
|
||||||
|
* Same as in [API usage](#Usage-of-the-API), but update the base esp-netif config `3c)` to enable DHCP client
|
||||||
|
```cpp
|
||||||
|
esp_netif_inherent_config_t base_cfg = {
|
||||||
|
.if_key = "TAP",
|
||||||
|
.flags = (esp_netif_flags_t)(ESP_NETIF_DHCP_CLIENT | ESP_NETIF_FLAG_EVENT_IP_MODIFIED | ESP_NETIF_FLAG_AUTOUP),
|
||||||
|
.route_prio = 100
|
||||||
|
};
|
||||||
|
```
|
||||||
|
* After starting the netif, tell the lwIP that we're connected
|
||||||
|
```cpp
|
||||||
|
esp_netif_action_connected(tap_netif, 0, 0, 0);
|
||||||
|
```
|
||||||
|
* Wait for the IP address to be assigned.
|
||||||
|
This could be implemented using an event handler
|
||||||
|
```cpp
|
||||||
|
esp_netif_inherent_config_t base_cfg = {
|
||||||
|
...
|
||||||
|
.get_ip_event = TAP0_GOT_IP,
|
||||||
|
...
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, TAP0_GOT_IP, event_handler, NULL));
|
||||||
|
// wait for the IP event (e.g. using signalling semaphores from the handler)
|
||||||
|
// ...
|
||||||
|
esp_netif_ip_info_t ip_info = {};
|
||||||
|
ESP_LOGI("tap-init", "Assigned IP address:"IPSTR ",", IP2STR(&ip_info.ip));
|
||||||
|
```
|
||||||
|
|
||||||
|
2) **Configure forwarding/routing** if needed based on the previous sections.
|
||||||
|
|
||||||
|
3) **Configure the DHCP server on the host machine**
|
||||||
|
|
||||||
|
Example for `isc-dhcp-server`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
INTERFACES="tap0";
|
||||||
|
authoritative;
|
||||||
|
|
||||||
|
subnet 192.168.5.0 netmask 255.255.255.0 {
|
||||||
|
range 192.168.5.2 192.168.5.200;
|
||||||
|
option routers 192.168.5.1;
|
||||||
|
option domain-name-servers 8.8.8.8;
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
/* Common functions for protocol examples, to establish tap interface connection
|
||||||
|
* For linux target
|
||||||
|
|
||||||
|
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, this
|
||||||
|
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "esp_err.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Configure and connect, wait for IP
|
||||||
|
*
|
||||||
|
* @return ESP_OK on successful connection
|
||||||
|
*/
|
||||||
|
esp_err_t example_connect(void);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_netif.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates tapio layer as a driver interface to esp-netif
|
||||||
|
*
|
||||||
|
* @warning Implemented as singleton, can use only one tapio in the system!
|
||||||
|
*
|
||||||
|
* @return pointer to the tapio driver handle
|
||||||
|
*/
|
||||||
|
void *tapio_create(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief esp-netif driver I/O output path
|
||||||
|
*
|
||||||
|
* @param h Driver's handle
|
||||||
|
* @param buffer Data to output
|
||||||
|
* @param len Data size
|
||||||
|
* @return ESP_OK on success
|
||||||
|
*/
|
||||||
|
esp_err_t tapio_output(void *h, void *buffer, size_t len);
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_netif.h"
|
||||||
|
|
||||||
|
// Use linux system sockets to connect to tap interface
|
||||||
|
#define LWIP_HDR_LINUX_SYS_SOCKETS_H
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <lwip/sys.h>
|
||||||
|
#include "errno.h"
|
||||||
|
|
||||||
|
#include <linux/if.h>
|
||||||
|
#include <linux/if_tun.h>
|
||||||
|
|
||||||
|
#define DEVTAP "/dev/net/tun"
|
||||||
|
#define DEVTAP_NAME "tap0"
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct tap_io {
|
||||||
|
esp_netif_driver_base_t base;
|
||||||
|
int fd;
|
||||||
|
} tap_io_t;
|
||||||
|
|
||||||
|
static const char *TAG = "tap-netif";
|
||||||
|
|
||||||
|
static void tapio_input_task(void *arg)
|
||||||
|
{
|
||||||
|
tap_io_t *io = arg;
|
||||||
|
fd_set fdset;
|
||||||
|
int ret;
|
||||||
|
while (1) {
|
||||||
|
FD_ZERO(&fdset);
|
||||||
|
FD_SET(io->fd, &fdset);
|
||||||
|
|
||||||
|
/* Wait for a packet to arrive. */
|
||||||
|
ret = select(io->fd + 1, &fdset, NULL, NULL, NULL);
|
||||||
|
|
||||||
|
if (ret == 1) {
|
||||||
|
/* Handle incoming packet. */
|
||||||
|
ssize_t readlen;
|
||||||
|
char buf[1518]; /* max packet size including VLAN excluding CRC */
|
||||||
|
|
||||||
|
/* Obtain the size of the packet and put it into the "len"
|
||||||
|
variable. */
|
||||||
|
readlen = read(io->fd, buf, sizeof(buf));
|
||||||
|
if (readlen < 0) {
|
||||||
|
ESP_LOGE(TAG, "Failed to read from tap fd: returned %ld", readlen);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
#if CONFIG_EXAMPLE_CONNECT_TAPIF_IN_LOSS
|
||||||
|
if (((double)rand()/(double)RAND_MAX) < ((double)CONFIG_EXAMPLE_CONNECT_TAPIF_IN_LOSS)/100.0) {
|
||||||
|
ESP_LOGW(TAG, "Simulated packet drop on input");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
esp_netif_receive(io->base.netif, buf, readlen, NULL);
|
||||||
|
} else if (ret == -1) {
|
||||||
|
if (errno == EINTR /* Interrupted system call (used by FreeRTOS simulated interrupts) */) {
|
||||||
|
vTaskDelay(1); // yield to the FreeRTOS simulator
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "tapif_thread: select() error(%d), %s", errno, strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t tapio_start(esp_netif_t *esp_netif, void *arg)
|
||||||
|
{
|
||||||
|
tap_io_t *io = arg;
|
||||||
|
io->base.netif = esp_netif;
|
||||||
|
esp_netif_action_start(esp_netif, 0, 0, 0);
|
||||||
|
|
||||||
|
sys_thread_new("tapio_rx", tapio_input_task, io, DEFAULT_THREAD_STACKSIZE, DEFAULT_THREAD_PRIO);
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *tapio_create(void)
|
||||||
|
{
|
||||||
|
static tap_io_t tap_io = {};
|
||||||
|
tap_io.base.post_attach = tapio_start;
|
||||||
|
tap_io.fd = open(DEVTAP, O_RDWR);
|
||||||
|
if (tap_io.fd == -1) {
|
||||||
|
ESP_LOGE(TAG, "Cannot open tap device %s", DEVTAP);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ifreq ifr = {};
|
||||||
|
memset(&ifr, 0, sizeof(ifr));
|
||||||
|
|
||||||
|
strncpy(ifr.ifr_name, DEVTAP_NAME, sizeof(ifr.ifr_name));
|
||||||
|
ifr.ifr_name[sizeof(ifr.ifr_name)-1] = 0; /* ensure \0 termination */
|
||||||
|
ifr.ifr_flags = IFF_TAP|IFF_NO_PI;
|
||||||
|
if (ioctl(tap_io.fd, TUNSETIFF, (void *) &ifr) < 0) {
|
||||||
|
ESP_LOGE(TAG, "Cannot configure ioctl(TUNSETIFF) for \"%s\"", DEVTAP);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return &tap_io;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t tapio_output(void *h, void *buffer, size_t len)
|
||||||
|
{
|
||||||
|
tap_io_t *io = h;
|
||||||
|
ssize_t written;
|
||||||
|
|
||||||
|
#if CONFIG_EXAMPLE_CONNECT_TAPIF_OUT_LOSS
|
||||||
|
if (((double)rand()/(double)RAND_MAX) < ((double)CONFIG_EXAMPLE_CONNECT_TAPIF_OUT_LOSS)/100.0) {
|
||||||
|
ESP_LOGW(TAG, "Simulated packet drop on output");
|
||||||
|
return ESP_OK; /* ESP_OK because we simulate packet loss on cable */
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* signal that packet should be sent(); */
|
||||||
|
written = write(io->fd, buffer, len);
|
||||||
|
if (written < len) {
|
||||||
|
ESP_LOGE(TAG, "Failed to write from tap fd: returned %ld", written);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_netif.h" // esp-netif
|
||||||
|
#include "tapio.h" // esp-netif's driver side
|
||||||
|
#include "lwip/tapif.h" // esp-netif's network stack side
|
||||||
|
#include "esp_log.h"
|
||||||
|
|
||||||
|
#if CONFIG_EXAMPLE_CONNECT_WAIT_FOR_IP
|
||||||
|
#include "esp_event.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "freertos/event_groups.h"
|
||||||
|
|
||||||
|
static const char *TAG = "linux_connect";
|
||||||
|
|
||||||
|
static EventGroupHandle_t s_events;
|
||||||
|
|
||||||
|
static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data)
|
||||||
|
{
|
||||||
|
xEventGroupSetBits(s_events, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CONFIG_EXAMPLE_CONNECT_WAIT_FOR_IP
|
||||||
|
|
||||||
|
#define TAP0_GOT_IP (0x1234)
|
||||||
|
|
||||||
|
esp_err_t example_connect(void)
|
||||||
|
{
|
||||||
|
#if CONFIG_EXAMPLE_CONNECT_LWIP_TAPIF
|
||||||
|
// configure linux tapio
|
||||||
|
esp_netif_driver_ifconfig_t driver_cfg = {
|
||||||
|
.handle = tapio_create(),
|
||||||
|
.transmit = tapio_output,
|
||||||
|
};
|
||||||
|
// configure lwip netif for the tapif
|
||||||
|
struct esp_netif_netstack_config stack_cfg = {
|
||||||
|
.lwip = {
|
||||||
|
.init_fn = lwip_tapif_init,
|
||||||
|
.input_fn = lwip_tapif_input,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// configure inherent esp-netif parameters
|
||||||
|
esp_netif_ip_info_t ip_info = {};
|
||||||
|
esp_netif_flags_t netif_flags = (ESP_NETIF_FLAG_EVENT_IP_MODIFIED | ESP_NETIF_FLAG_AUTOUP);
|
||||||
|
#if !CONFIG_EXAMPLE_CONNECT_WAIT_FOR_IP
|
||||||
|
ip_info.ip.addr = ipaddr_addr(CONFIG_EXAMPLE_CONNECT_TAPIF_IP_ADDR);
|
||||||
|
ip_info.netmask.addr = ipaddr_addr(CONFIG_EXAMPLE_CONNECT_TAPIF_NETMASK);
|
||||||
|
ip_info.gw.addr = ipaddr_addr(CONFIG_EXAMPLE_CONNECT_TAPIF_GW);
|
||||||
|
#else
|
||||||
|
netif_flags |= ESP_NETIF_DHCP_CLIENT;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
esp_netif_inherent_config_t base_cfg = {
|
||||||
|
.if_key = "TAP",
|
||||||
|
.ip_info = &ip_info,
|
||||||
|
.flags = netif_flags,
|
||||||
|
.get_ip_event = TAP0_GOT_IP,
|
||||||
|
.route_prio = 100
|
||||||
|
};
|
||||||
|
|
||||||
|
// put all configs together
|
||||||
|
esp_netif_config_t cfg = {
|
||||||
|
.base = &base_cfg,
|
||||||
|
.driver = &driver_cfg,
|
||||||
|
.stack = &stack_cfg
|
||||||
|
};
|
||||||
|
|
||||||
|
// create the interface and attach it to the tapio-handle
|
||||||
|
esp_netif_t *tap_netif = esp_netif_new(&cfg);
|
||||||
|
esp_netif_attach(tap_netif, driver_cfg.handle);
|
||||||
|
#if CONFIG_EXAMPLE_CONNECT_WAIT_FOR_IP
|
||||||
|
ESP_LOGI(TAG, "Waiting for IP addresses...");
|
||||||
|
esp_netif_action_connected(tap_netif, 0, 0, 0);
|
||||||
|
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, TAP0_GOT_IP, event_handler, NULL));
|
||||||
|
s_events = xEventGroupCreate();
|
||||||
|
xEventGroupWaitBits(s_events, 1, pdFALSE, pdFALSE, portMAX_DELAY);
|
||||||
|
esp_netif_get_ip_info(tap_netif, &ip_info);
|
||||||
|
ESP_LOGI(TAG, "Assigned IP address:"IPSTR ",", IP2STR(&ip_info.ip));
|
||||||
|
#endif // CONFIG_EXAMPLE_CONNECT_WAIT_FOR_IP
|
||||||
|
#endif // EXAMPLE_CONNECT_LWIP_TAPIF
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2001-2003 Swedish Institute of Computer Science
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*
|
||||||
|
* SPDX-FileContributor: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*/
|
||||||
|
#include <string.h>
|
||||||
|
#include "lwip/opt.h"
|
||||||
|
#include "lwip/pbuf.h"
|
||||||
|
#include "lwip/snmp.h"
|
||||||
|
#include "lwip/ethip6.h"
|
||||||
|
#include "netif/etharp.h"
|
||||||
|
|
||||||
|
#include "esp_netif.h"
|
||||||
|
#include "esp_netif_net_stack.h"
|
||||||
|
|
||||||
|
#define IFNAME0 't'
|
||||||
|
#define IFNAME1 'p'
|
||||||
|
|
||||||
|
static err_t
|
||||||
|
low_level_output(struct netif *netif, struct pbuf *p)
|
||||||
|
{
|
||||||
|
esp_netif_t *esp_netif = esp_netif_get_handle_from_netif_impl(netif);
|
||||||
|
char buf[1518]; /* max packet size including VLAN excluding CRC */
|
||||||
|
|
||||||
|
if (p->tot_len > sizeof(buf)) {
|
||||||
|
MIB2_STATS_NETIF_INC(netif, ifoutdiscards);
|
||||||
|
LWIP_DEBUGF(NETIF_DEBUG, ("tapif: packet too large"));
|
||||||
|
return ERR_IF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* initiate transfer(); */
|
||||||
|
pbuf_copy_partial(p, buf, p->tot_len, 0);
|
||||||
|
|
||||||
|
int ret = esp_netif_transmit(esp_netif, buf, p->tot_len);
|
||||||
|
/* Check error */
|
||||||
|
if (likely(ret == ESP_OK)) {
|
||||||
|
return ERR_OK;
|
||||||
|
}
|
||||||
|
if (ret == ESP_ERR_NO_MEM) {
|
||||||
|
return ERR_MEM;
|
||||||
|
}
|
||||||
|
return ERR_IF;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
low_level_init(struct netif *netif)
|
||||||
|
{
|
||||||
|
|
||||||
|
/* Obtain MAC address from network interface. */
|
||||||
|
netif->hwaddr[0] = 0x02;
|
||||||
|
netif->hwaddr[1] = 0x12;
|
||||||
|
netif->hwaddr[2] = 0x34;
|
||||||
|
netif->hwaddr[3] = 0x56;
|
||||||
|
netif->hwaddr[4] = 0x78;
|
||||||
|
netif->hwaddr[5] = 0xab;
|
||||||
|
netif->hwaddr_len = 6;
|
||||||
|
|
||||||
|
/* device capabilities */
|
||||||
|
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_IGMP;
|
||||||
|
}
|
||||||
|
|
||||||
|
err_t lwip_tapif_init(struct netif *netif)
|
||||||
|
{
|
||||||
|
LWIP_ASSERT("Tried to initialize tapif with NULL netif", netif != NULL);
|
||||||
|
netif->name[0] = IFNAME0;
|
||||||
|
netif->name[1] = IFNAME1;
|
||||||
|
#if LWIP_IPV4
|
||||||
|
netif->output = etharp_output;
|
||||||
|
#endif /* LWIP_IPV4 */
|
||||||
|
#if LWIP_IPV6
|
||||||
|
netif->output_ip6 = ethip6_output;
|
||||||
|
#endif /* LWIP_IPV6 */
|
||||||
|
netif->linkoutput = low_level_output;
|
||||||
|
netif->mtu = 1500;
|
||||||
|
|
||||||
|
low_level_init(netif);
|
||||||
|
netif_set_link_up(netif);
|
||||||
|
return ERR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t lwip_tapif_input(void *h, void *buffer, size_t len, void *l2_buff)
|
||||||
|
{
|
||||||
|
struct netif *netif = h;
|
||||||
|
struct pbuf *p;
|
||||||
|
LWIP_ASSERT("running tapif input with NULL netif", netif != NULL);
|
||||||
|
|
||||||
|
p = pbuf_alloc(PBUF_RAW, len, PBUF_RAM);
|
||||||
|
if (p == NULL) {
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
memcpy(p->payload, buffer, len);
|
||||||
|
|
||||||
|
/* full packet send to tcpip_thread to process */
|
||||||
|
if (unlikely(netif->input(p, netif) != ERR_OK)) {
|
||||||
|
LWIP_DEBUGF(NETIF_DEBUG, ("tapif_input: IP input error\n"));
|
||||||
|
pbuf_free(p);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2001-2003 Swedish Institute of Computer Science
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*
|
||||||
|
* SPDX-FileContributor: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "lwip/esp_netif_net_stack.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief lwip netif init API
|
||||||
|
* @param netif pointer to lwip's netif
|
||||||
|
* @return ERR_OK on success
|
||||||
|
*/
|
||||||
|
err_t lwip_tapif_init(struct netif *netif);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Input data path
|
||||||
|
* @param h pointer to network stack handle (stuct netif* in our case)
|
||||||
|
* @param buffer Data
|
||||||
|
* @param len Data size
|
||||||
|
* @param l2_buff Data L2 buffer
|
||||||
|
*/
|
||||||
|
esp_err_t lwip_tapif_input(void *h, void *buffer, size_t len, void *l2_buff);
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
sudo ip tuntap add dev tap0 mode tap user `whoami`
|
||||||
|
sudo ip link set tap0 up
|
||||||
|
sudo ip addr add 192.168.5.1/24 dev tap0
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "ping.h"
|
#include "ping.h"
|
||||||
#include "esp_http_server.h"
|
#include "esp_http_server.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
static const char *TAG = "web_server";
|
static const char *TAG = "web_server";
|
||||||
|
|
||||||
@@ -88,7 +89,7 @@ static esp_err_t main_page_handler(httpd_req_t *req)
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
return httpd_resp_sendstr_chunk(req, NULL);;
|
return httpd_resp_sendstr_chunk(req, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t send_page_header(httpd_req_t *req)
|
static esp_err_t send_page_header(httpd_req_t *req)
|
||||||
@@ -123,6 +124,9 @@ static esp_err_t send_device_table(httpd_req_t *req)
|
|||||||
"</tr>"
|
"</tr>"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (err != ESP_OK)
|
||||||
|
return err;
|
||||||
|
|
||||||
for (size_t index = 0; index < device_list_count(); index++)
|
for (size_t index = 0; index < device_list_count(); index++)
|
||||||
{
|
{
|
||||||
const device_info_t *current_device;
|
const device_info_t *current_device;
|
||||||
@@ -156,7 +160,7 @@ static esp_err_t send_device_row(httpd_req_t *req, const device_info_t *device)
|
|||||||
|
|
||||||
"<tr>"
|
"<tr>"
|
||||||
"<td>"
|
"<td>"
|
||||||
"<input type=\"checkbox\" name=\"mac\" value=\"%02X:%02X:%02X:%02X:%02X:%02X\">"
|
"<input type=\"checkbox\" name=\"mac\" value=\"%02X%02X%02X%02X%02X%02X\">"
|
||||||
"</td>"
|
"</td>"
|
||||||
"<td>%u.%u.%u.%u</td>"
|
"<td>%u.%u.%u.%u</td>"
|
||||||
"<td>%02X:%02X:%02X:%02X:%02X:%02X</td>"
|
"<td>%02X:%02X:%02X:%02X:%02X:%02X</td>"
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 49 KiB |
+15
-4
@@ -1,8 +1,19 @@
|
|||||||
idf_component_register(
|
idf_build_get_property(target IDF_TARGET)
|
||||||
SRCS "main.c"
|
|
||||||
INCLUDE_DIRS "."
|
set(requires
|
||||||
REQUIRES
|
|
||||||
arp_monitor
|
arp_monitor
|
||||||
device_list
|
device_list
|
||||||
web_server
|
web_server
|
||||||
|
esp_netif
|
||||||
|
esp_event
|
||||||
|
)
|
||||||
|
|
||||||
|
if("${target}" STREQUAL "linux")
|
||||||
|
list(APPEND requires protocol_examples_tapif_io)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
idf_component_register(
|
||||||
|
SRCS "main.c"
|
||||||
|
INCLUDE_DIRS "."
|
||||||
|
REQUIRES ${requires}
|
||||||
)
|
)
|
||||||
+19
-9
@@ -1,20 +1,30 @@
|
|||||||
#include "arp_monitor.h"
|
#include "arp_monitor.h"
|
||||||
#include "device_list.h"
|
#include "device_list.h"
|
||||||
#include "web_server.h"
|
#include "web_server.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_event.h"
|
||||||
|
#include "esp_netif.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
|
||||||
|
#ifdef CONFIG_IDF_TARGET_LINUX
|
||||||
|
#include "protocol_examples_common.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const char *TAG = "main";
|
||||||
|
|
||||||
void app_main(void)
|
void app_main(void)
|
||||||
{
|
{
|
||||||
device_list_init();
|
device_list_init();
|
||||||
arp_monitor_init();
|
arp_monitor_init();
|
||||||
|
|
||||||
|
#ifdef CONFIG_IDF_TARGET_LINUX
|
||||||
|
ESP_ERROR_CHECK(esp_netif_init());
|
||||||
|
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||||
|
ESP_ERROR_CHECK(example_connect());
|
||||||
|
ESP_LOGI(TAG, "Linux network interface initialized");
|
||||||
|
#else
|
||||||
|
ESP_LOGW(TAG, "Network initialization for the ESP32 target is not configured");
|
||||||
|
#endif
|
||||||
|
|
||||||
web_server_init();
|
web_server_init();
|
||||||
|
|
||||||
arp_event_t event = {0};
|
|
||||||
IP4_ADDR(&event.ip, 192, 168, 1, 10);
|
|
||||||
uint8_t mac[6] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF};
|
|
||||||
memcpy(event.mac, mac, ETH_HWADDR_LEN);
|
|
||||||
|
|
||||||
arp_monitor_post_event(&event);
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(100));
|
|
||||||
ESP_LOGI("TEST", "Count = %u", device_list_count());
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
CONFIG_LWIP_ENABLE=y
|
||||||
|
CONFIG_ESP_NETIF_TCPIP_LWIP=y
|
||||||
|
CONFIG_EXAMPLE_CONNECT_LWIP_TAPIF=y
|
||||||
|
CONFIG_EXAMPLE_CONNECT_IPV4=y
|
||||||
|
# CONFIG_EXAMPLE_CONNECT_IPV6 is not set
|
||||||
|
# CONFIG_EXAMPLE_CONNECT_WAIT_FOR_IP is not set
|
||||||
|
CONFIG_EXAMPLE_CONNECT_TAPIF_IP_ADDR="192.168.5.100"
|
||||||
|
CONFIG_EXAMPLE_CONNECT_TAPIF_NETMASK="255.255.255.0"
|
||||||
|
CONFIG_EXAMPLE_CONNECT_TAPIF_GW="192.168.5.1"
|
||||||
Reference in New Issue
Block a user