esp-idf/examples/common_components/protocol_examples_tapif_io/README.md
David Cermak 854e16feb3 examples: Add common linux component tapif_io
That can be used with linux target on lwip to pass packets from lwip to
linux host networking stack, e.g. routing the trafic to internet.
2023-01-31 08:43:50 +01:00

6.4 KiB

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 the EXTRA_COMPONENT_DIRS in your project makefile
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/examples/common_components/tapif_io")
  1. Include lwip and linux side of the configuration
#include "esp_netif.h"      // esp-netif
#include "tapio.h"          // esp-netif's driver side
#include "lwip/tapif.h"     // esp-netif's network stack side
  1. Configure the esp-netif a) setup the linux tap I/O config
    esp_netif_driver_ifconfig_t driver_cfg = {
            .handle = tapio_create(),
            .transmit = tapio_output,
    };

b) configure the lwip netif for the tap interface

    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

    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
    };
  1. Initialize and attach the esp_netif to the I/O handle
    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.
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:

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).
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.

  1. Configure and set the esp-netif up
  • Same as in API usage, but update the base esp-netif config 3c) to enable DHCP client
    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
    esp_netif_action_connected(tap_netif, 0, 0, 0);
  • Wait for the IP address to be assigned. This could be implemented as a wait loop below, as the esp-event currently doesn't support IP events on Linux target.
    esp_netif_ip_info_t ip_info = {};
    while (ip_info.ip.addr == 0) {
        ESP_LOGI("tap-init", "No IP assigned, waiting...");
        usleep(1000000);
        esp_netif_get_ip_info(tap_netif, &ip_info);
    }
    ESP_LOGI("tap-init", "Assigned IP address:"IPSTR ",", IP2STR(&ip_info.ip));
  1. Configure forwarding/routing if needed based on the previous sections.

  2. Configure the DHCP server on the host machine

Example for isc-dhcp-server

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;
}