UART Tunnel Network Interface Design Specification¶
This document specifies the component-level design of the netif_uart_tunnel
component, which provides full TCP/IP stack functionality in QEMU emulation by
tunnelling IP packets (encapsulated in Ethernet frames) over UART1.
Document Version: 1.0 Last Updated: 2025-03-16
Source files reviewed:
main/components/netif_uart_tunnel/netif_uart_tunnel_sim.cmain/components/netif_uart_tunnel/netif_uart_tunnel_sim.htools/qemu/network/serial_tun_bridge.pymain/components/netif_uart_tunnel/README.md
This spec is a documentation catch-up — the code is stable and correct. All SPECs below describe what the implementation actually does. Two known deviations from requirements are documented honestly: the static IP configuration (vs DHCP) and the 115200 baud rate (vs 100 KB/s target).
See Network Tunnel Component De... (SPEC_ARCH_NETIF_1) for the high-level architectural overview.
Component Architecture¶
Description: The Component Location: Key Files:
Threading Model:
Data Flow: ┌─────────────────────────────────────────────────────────────┐
│ ESP32 (QEMU) │
│ Application → lwIP → etharp_output → netif_linkoutput │
│ │ │
│ UART1 TX (GPIO17) │
└─────────────────────────────────────────────────────────────┘
↕ Unix socket (temp/esp32-uart1.sock)
┌─────────────────────────────────────────────────────────────┐
│ Host (Linux) │
│ serial_tun_bridge.py │
│ UART socket ↔ TUN device (tun0) │
│ tun0: 192.168.100.1/24 │
└─────────────────────────────────────────────────────────────┘
UART Configuration (hardcoded in implementation):
Note The header file comment mentions GPIO4/GPIO5 as defaults; the actual implementation uses GPIO17/GPIO16. The code values take precedence. Network Configuration (default, passed by caller in
lwIP Interface Flags:
The interface operates as a full Ethernet interface so that lwIP’s ARP layer produces complete Ethernet frames. The host-side bridge strips/adds the Ethernet header when interfacing with the TUN device (which expects raw IP packets). Static ARP Entry for Gateway: An ARP entry is pre-populated for the gateway MAC ( Lifecycle:
Known Limitation: This component is QEMU-only. It MUST NOT be linked in hardware builds. See Conditional Compilation — Q... (SPEC_NETIF_UART_COND_1) for build guard details. |
Wire Protocol¶
Description: IP packets (wrapped in Ethernet frames) are transported over UART1 using a simple 2-byte length-prefix framing protocol. There is no checksum or CRC at this layer — TCP handles end-to-end integrity. Frame Format: ┌─────────────────┬────────────────────────────────────────────┐
│ LENGTH (2 B) │ DATA (N bytes — complete Ethernet frame) │
│ big-endian │ N = 14 (Ethernet header) + IP payload │
│ uint16_t │ max N = 1500 bytes (MTU) │
└─────────────────┴────────────────────────────────────────────┘
Ethernet Encapsulation: The DATA field is a complete Ethernet frame, not a raw IP packet. lwIP operates
as an Ethernet interface ( The Python bridge strips the 14-byte Ethernet header before writing to the TUN device (which handles raw IP), and prepends a synthetic Ethernet header when forwarding packets from TUN to the serial socket. Framing Rules:
TX Path (ESP32 → Host):
RX Path (Host → ESP32):
Error Recovery:
Startup Delay: After |
Host-Side Bridge¶
Description: Script Location: Prerequisites:
Note
Architecture: QEMU UART1 (Unix socket at temp/esp32-uart1.sock)
↕ Ethernet frames with 2-byte length prefix
SerialTunBridge.serial_to_tun() / tun_to_serial()
↕ Raw IP packets (Ethernet header stripped / added)
TUN device tun0 (192.168.100.1/24)
↕
Linux host network stack
TUN Device:
Ethernet/IP Translation:
Socket Connection:
Execution Modes:
Known Limitations:
|
IP Configuration¶
Description: The tunnel interface uses static IP configuration. The DHCP
client is explicitly disabled. Implemented Behaviour:
Static ARP Entry for Gateway: A static ARP entry is pre-populated for the gateway to avoid ARP request/response cycles that cannot succeed over a point-to-point UART link: struct eth_addr gw_mac = {{0x02, 0x00, 0x00, 0x00, 0x00, 0x01}};
etharp_add_static_entry(&gw_ip, &gw_mac);
Rationale for Static Configuration: A UART tunnel is a point-to-point link. DHCP discovery would require the host-side bridge to run a DHCP server. Static configuration is simpler, deterministic, and fully sufficient for emulation purposes. AC Coverage vs REQ_NETIF_TUNNEL_4:
Note AC-1 through AC-3 of |
Conditional Compilation¶
Description: The tunnel component is conditionally included in the build using CMake and Kconfig guards, ensuring zero overhead on hardware builds. Application-Level Guard in #ifdef CONFIG_TARGET_EMULATOR
netif_uart_tunnel_init(&tunnel_config);
#else
// WiFi init for real hardware
wifi_manager_init();
#endif
The CMakeLists.txt Guard: The component’s Kconfig Options (from README):
Result on Hardware Builds:
|
Performance and Known Limitations¶
Description: Documents the measured and expected performance of the UART tunnel and all known limitations relevant to emulation use. Throughput:
Packet Loss Handling:
RX Task Design:
Known Limitations:
|
Documentation¶
Description: Documents where emulation setup instructions are maintained and what they cover. Documentation Locations:
Coverage by Requirement AC:
|
Traceability¶
All traceability is automatically generated by Sphinx-Needs based on the
:links: attributes in each SPEC above.
ID |
Title |
Status |
Tags |
|---|---|---|---|
UART Tunnel Component Architecture |
implemented |
netif; qemu; uart; architecture |
|
Host-Side Serial-TUN Bridge Script |
implemented |
netif; qemu; python; bridge; tooling |
|
Conditional Compilation — QEMU-Only Build Guard |
implemented |
netif; qemu; build; kconfig |
|
IP Configuration and DHCP Client Integration |
implemented |
netif; qemu; dhcp; ip |
|
Emulation Setup Documentation |
implemented |
netif; qemu; documentation |
|
Performance Characteristics and Known Limitations |
implemented |
netif; qemu; performance; limitations |
|
UART Wire Protocol — Length-Prefix Framing |
implemented |
netif; qemu; protocol; framing |