tapio.c 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. /*
  2. * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. #include <stdlib.h>
  7. #include <unistd.h>
  8. #include <string.h>
  9. #include "esp_err.h"
  10. #include "esp_log.h"
  11. #include "esp_netif.h"
  12. // Use linux system sockets to connect to tap interface
  13. #define LWIP_HDR_LINUX_SYS_SOCKETS_H
  14. #include <sys/socket.h>
  15. #include <fcntl.h>
  16. #include <sys/ioctl.h>
  17. #include <sys/types.h>
  18. #include <sys/ioctl.h>
  19. #include <lwip/sys.h>
  20. #include "errno.h"
  21. #include <linux/if.h>
  22. #include <linux/if_tun.h>
  23. #define DEVTAP "/dev/net/tun"
  24. #define DEVTAP_NAME "tap0"
  25. typedef struct tap_io {
  26. esp_netif_driver_base_t base;
  27. int fd;
  28. } tap_io_t;
  29. static const char *TAG = "tap-netif";
  30. static void tapio_input_task(void *arg)
  31. {
  32. tap_io_t *io = arg;
  33. fd_set fdset;
  34. int ret;
  35. while (1) {
  36. FD_ZERO(&fdset);
  37. FD_SET(io->fd, &fdset);
  38. /* Wait for a packet to arrive. */
  39. ret = select(io->fd + 1, &fdset, NULL, NULL, NULL);
  40. if (ret == 1) {
  41. /* Handle incoming packet. */
  42. ssize_t readlen;
  43. char buf[1518]; /* max packet size including VLAN excluding CRC */
  44. /* Obtain the size of the packet and put it into the "len"
  45. variable. */
  46. readlen = read(io->fd, buf, sizeof(buf));
  47. if (readlen < 0) {
  48. ESP_LOGE(TAG, "Failed to read from tap fd: returned %ld", readlen);
  49. exit(1);
  50. }
  51. #if CONFIG_EXAMPLE_CONNECT_TAPIF_IN_LOSS
  52. if (((double)rand()/(double)RAND_MAX) < ((double)CONFIG_EXAMPLE_CONNECT_TAPIF_IN_LOSS)/100.0) {
  53. ESP_LOGW(TAG, "Simulated packet drop on input");
  54. continue;
  55. }
  56. #endif
  57. esp_netif_receive(io->base.netif, buf, readlen, NULL);
  58. } else if (ret == -1) {
  59. if (errno == EINTR /* Interrupted system call (used by FreeRTOS simulated interrupts) */) {
  60. vTaskDelay(1); // yield to the FreeRTOS simulator
  61. } else {
  62. ESP_LOGE(TAG, "tapif_thread: select() error(%d), %s", errno, strerror(errno));
  63. }
  64. }
  65. }
  66. }
  67. static esp_err_t tapio_start(esp_netif_t *esp_netif, void *arg)
  68. {
  69. tap_io_t *io = arg;
  70. io->base.netif = esp_netif;
  71. esp_netif_action_start(esp_netif, 0, 0, 0);
  72. sys_thread_new("tapio_rx", tapio_input_task, io, DEFAULT_THREAD_STACKSIZE, DEFAULT_THREAD_PRIO);
  73. return ESP_OK;
  74. }
  75. void *tapio_create(void)
  76. {
  77. static tap_io_t tap_io = {};
  78. tap_io.base.post_attach = tapio_start;
  79. tap_io.fd = open(DEVTAP, O_RDWR);
  80. if (tap_io.fd == -1) {
  81. ESP_LOGE(TAG, "Cannot open tap device %s", DEVTAP);
  82. return NULL;
  83. }
  84. struct ifreq ifr = {};
  85. memset(&ifr, 0, sizeof(ifr));
  86. strncpy(ifr.ifr_name, DEVTAP_NAME, sizeof(ifr.ifr_name));
  87. ifr.ifr_name[sizeof(ifr.ifr_name)-1] = 0; /* ensure \0 termination */
  88. ifr.ifr_flags = IFF_TAP|IFF_NO_PI;
  89. if (ioctl(tap_io.fd, TUNSETIFF, (void *) &ifr) < 0) {
  90. ESP_LOGE(TAG, "Cannot configure ioctl(TUNSETIFF) for \"%s\"", DEVTAP);
  91. return NULL;
  92. }
  93. return &tap_io;
  94. }
  95. esp_err_t tapio_output(void *h, void *buffer, size_t len)
  96. {
  97. tap_io_t *io = h;
  98. ssize_t written;
  99. #if CONFIG_EXAMPLE_CONNECT_TAPIF_OUT_LOSS
  100. if (((double)rand()/(double)RAND_MAX) < ((double)CONFIG_EXAMPLE_CONNECT_TAPIF_OUT_LOSS)/100.0) {
  101. ESP_LOGW(TAG, "Simulated packet drop on output");
  102. return ESP_OK; /* ESP_OK because we simulate packet loss on cable */
  103. }
  104. #endif
  105. /* signal that packet should be sent(); */
  106. written = write(io->fd, buffer, len);
  107. if (written < len) {
  108. ESP_LOGE(TAG, "Failed to write from tap fd: returned %ld", written);
  109. return ESP_FAIL;
  110. }
  111. return ESP_OK;
  112. }