esp-idf/examples/common_components/protocol_examples_tapif_io/linux/tapio.c
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

136 lines
3.7 KiB
C

/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include "esp_err.h"
#include "esp_log.h"
#include <stdlib.h>
#include "esp_netif.h"
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <lwip/sys.h>
#include "errno.h"
#define LWIP_HDR_LINUX_SYS_SOCKETS_H
#include <linux/if.h>
#include <linux/if_tun.h>
#include <esp_netif_net_stack.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;
}