esp-idf/examples/common_components/protocol_examples_tapif_io/README.md

155 lines
6.4 KiB
Markdown
Raw Normal View History

# 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
```cmake
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{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.
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 as a wait loop below, as the esp-event currently doesn't support IP events on Linux target.
```cpp
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));
```
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;
}
```