/* * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #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 #include #include #include #include #include #include "errno.h" #include #include #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; }