l2tap_main.c 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. /*
  2. * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Unlicense OR CC0-1.0
  5. */
  6. #include <stdio.h>
  7. #include <string.h>
  8. #include <unistd.h> // read/write
  9. #include <sys/fcntl.h>
  10. #include <sys/ioctl.h>
  11. #include <errno.h>
  12. #include "freertos/FreeRTOS.h"
  13. #include "freertos/task.h"
  14. #include "esp_event.h"
  15. #include "esp_err.h"
  16. #include "esp_eth.h"
  17. #include "esp_log.h"
  18. #include "nvs_flash.h"
  19. #include "sdkconfig.h"
  20. #include "esp_vfs_l2tap.h"
  21. #include "lwip/prot/ethernet.h" // Ethernet header
  22. #include "arpa/inet.h" // ntohs, etc.
  23. #include "protocol_examples_common.h"
  24. #if !defined(CONFIG_EXAMPLE_CONNECT_ETHERNET)
  25. #error Ethernet interface is not configured to connect.
  26. #endif
  27. #define ETH_INTERFACE "ETH_DEF"
  28. #define ETH_TYPE_FILTER_BLOCK 0x2220
  29. #define ETH_TYPE_FILTER_NOBLOCK 0x2221
  30. #define ETH_TYPE_FILTER_TX 0x2223
  31. #define INVALID_FD -1
  32. typedef struct {
  33. struct eth_hdr header;
  34. char payload[44];
  35. } test_vfs_eth_tap_msg_t;
  36. static const char *TAG = "l2tap_example";
  37. /** Opens and configures L2 TAP file descriptor */
  38. static int init_l2tap_fd(int flags, uint16_t eth_type_filter)
  39. {
  40. int fd = open("/dev/net/tap", flags);
  41. if (fd < 0) {
  42. ESP_LOGE(TAG, "Unable to open L2 TAP interface: errno %d", errno);
  43. goto error;
  44. }
  45. ESP_LOGI(TAG, "/dev/net/tap fd %d successfully opened", fd);
  46. // Check fd block status (just for demonstration purpose)
  47. flags = 0;
  48. flags = fcntl(fd, F_GETFL);
  49. if (flags == -1) {
  50. ESP_LOGE(TAG, "Unable to get L2 TAP fd %d status flag: errno %d", fd, errno);
  51. goto error;
  52. }
  53. if (flags & O_NONBLOCK) {
  54. ESP_LOGI(TAG, "L2 TAP fd %d configured in non-blocking mode", fd);
  55. } else {
  56. ESP_LOGI(TAG, "L2 TAP fd %d configured in blocking mode", fd);
  57. }
  58. // Configure Ethernet interface on which to get raw frames
  59. int ret;
  60. if ((ret = ioctl(fd, L2TAP_S_INTF_DEVICE, ETH_INTERFACE)) == -1) {
  61. ESP_LOGE(TAG, "Unable to bound L2 TAP fd %d with Ethernet device: errno %d", fd, errno);
  62. goto error;
  63. }
  64. ESP_LOGI(TAG, "L2 TAP fd %d successfully bound to `%s`", fd, ETH_INTERFACE);
  65. // Configure Ethernet frames we want to filter out
  66. if ((ret = ioctl(fd, L2TAP_S_RCV_FILTER, &eth_type_filter)) == -1) {
  67. ESP_LOGE(TAG, "Unable to configure fd %d Ethernet type receive filter: errno %d", fd, errno);
  68. goto error;
  69. }
  70. ESP_LOGI(TAG, "L2 TAP fd %d Ethernet type filter configured to 0x%x", fd, eth_type_filter);
  71. return fd;
  72. error:
  73. if (fd != INVALID_FD) {
  74. close(fd);
  75. }
  76. return INVALID_FD;
  77. }
  78. /** Creates "echo" message from received frame */
  79. static void create_echo_frame(test_vfs_eth_tap_msg_t *in_frame, test_vfs_eth_tap_msg_t *out_frame, int len)
  80. {
  81. // Set source address equal to our MAC address
  82. esp_eth_handle_t eth_hndl = get_example_eth_handle();
  83. uint8_t mac_addr[ETH_ADDR_LEN];
  84. esp_eth_ioctl(eth_hndl, ETH_CMD_G_MAC_ADDR, mac_addr);
  85. memcpy(out_frame->header.src.addr, mac_addr, ETH_ADDR_LEN);
  86. // Set destination address equal to source address from where the frame was received
  87. memcpy(out_frame->header.dest.addr, in_frame->header.src.addr, ETH_ADDR_LEN);
  88. // Set Ethernet type
  89. memcpy(&out_frame->header.type, &in_frame->header.type, sizeof(uint16_t));
  90. // Copy the payload
  91. memcpy(out_frame->payload, in_frame->payload, len - ETH_HEADER_LEN);
  92. }
  93. /** Demonstrates usage of L2 TAP in blocking mode */
  94. static void echo_l2tap_task(void *pvParameters)
  95. {
  96. uint8_t rx_buffer[128];
  97. int eth_tap_fd;
  98. // Open and configure L2 TAP File descriptor
  99. if ((eth_tap_fd = init_l2tap_fd(0, ETH_TYPE_FILTER_BLOCK)) == INVALID_FD) {
  100. goto error;
  101. }
  102. while (1) {
  103. ssize_t len = read(eth_tap_fd, rx_buffer, sizeof(rx_buffer));
  104. if (len > 0) {
  105. test_vfs_eth_tap_msg_t *recv_msg = (test_vfs_eth_tap_msg_t *)rx_buffer;
  106. ESP_LOGI(TAG, "fd %d received %d bytes from %.2x:%.2x:%.2x:%.2x:%.2x:%.2x", eth_tap_fd,
  107. len, recv_msg->header.src.addr[0], recv_msg->header.src.addr[1], recv_msg->header.src.addr[2],
  108. recv_msg->header.src.addr[3], recv_msg->header.src.addr[4], recv_msg->header.src.addr[5]);
  109. // Construct echo frame
  110. test_vfs_eth_tap_msg_t echo_msg;
  111. create_echo_frame(recv_msg, &echo_msg, len);
  112. // Send the echo message
  113. ssize_t ret = write(eth_tap_fd, &echo_msg, len);
  114. if (ret == -1) {
  115. ESP_LOGE(TAG, "L2 TAP fd %d write error: errno: %d", eth_tap_fd, errno);
  116. break;
  117. }
  118. } else {
  119. ESP_LOGE(TAG, "L2 TAP fd %d read error: errno %d", eth_tap_fd, errno);
  120. break;
  121. }
  122. }
  123. close(eth_tap_fd);
  124. error:
  125. vTaskDelete(NULL);
  126. }
  127. /** Demonstrates usage of L2 TAP non-blocking mode with select */
  128. static void nonblock_l2tap_echo_task(void *pvParameters)
  129. {
  130. uint8_t rx_buffer[128];
  131. int eth_tap_fd;
  132. // Open and configure L2 TAP File descriptor
  133. if ((eth_tap_fd = init_l2tap_fd(O_NONBLOCK, ETH_TYPE_FILTER_NOBLOCK)) == INVALID_FD) {
  134. goto error;
  135. }
  136. while (1) {
  137. struct timeval tv;
  138. tv.tv_sec = 5;
  139. tv.tv_usec = 0;
  140. fd_set rfds;
  141. FD_ZERO(&rfds);
  142. FD_SET(eth_tap_fd, &rfds);
  143. int ret_sel = select(eth_tap_fd + 1, &rfds, NULL, NULL, &tv);
  144. if (ret_sel > 0) {
  145. ssize_t len = read(eth_tap_fd, rx_buffer, sizeof(rx_buffer));
  146. if (len > 0) {
  147. test_vfs_eth_tap_msg_t *recv_msg = (test_vfs_eth_tap_msg_t *)rx_buffer;
  148. ESP_LOGI(TAG, "fd %d received %d bytes from %.2x:%.2x:%.2x:%.2x:%.2x:%.2x", eth_tap_fd,
  149. len, recv_msg->header.src.addr[0], recv_msg->header.src.addr[1], recv_msg->header.src.addr[2],
  150. recv_msg->header.src.addr[3], recv_msg->header.src.addr[4], recv_msg->header.src.addr[5]);
  151. // Construct echo frame
  152. test_vfs_eth_tap_msg_t echo_msg;
  153. create_echo_frame(recv_msg, &echo_msg, len);
  154. // Send the echo message
  155. ssize_t ret = write(eth_tap_fd, &echo_msg, len);
  156. if (ret == -1) {
  157. ESP_LOGE(TAG, "L2 TAP fd %d write error: errno: %d", eth_tap_fd, errno);
  158. break;
  159. }
  160. } else {
  161. ESP_LOGE(TAG, "L2 TAP fd %d read error: errno %d", eth_tap_fd, errno);
  162. break;
  163. }
  164. } else if (ret_sel == 0) {
  165. ESP_LOGD(TAG, "L2 TAP select timeout");
  166. } else {
  167. ESP_LOGE(TAG, "L2 TAP select error: errno %d", errno);
  168. break;
  169. }
  170. }
  171. close(eth_tap_fd);
  172. error:
  173. vTaskDelete(NULL);
  174. }
  175. /** Demonstrates of how to construct Ethernet frame for transmit via L2 TAP */
  176. static void hello_tx_l2tap_task(void *pvParameters)
  177. {
  178. uint16_t eth_type_filter = ETH_TYPE_FILTER_TX;
  179. int eth_tap_fd;
  180. // Open and configure L2 TAP File descriptor
  181. if ((eth_tap_fd = init_l2tap_fd(0, eth_type_filter)) == INVALID_FD) {
  182. goto error;
  183. }
  184. // Construct "Hello" frame
  185. test_vfs_eth_tap_msg_t hello_msg = {
  186. .header = {
  187. .src.addr = {0},
  188. .dest.addr = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, // broadcast address
  189. .type = htons(eth_type_filter) // convert to big endian (network) byte order
  190. },
  191. .payload = "ESP32 hello to everybody!"
  192. };
  193. esp_eth_handle_t eth_hndl = get_example_eth_handle();
  194. esp_eth_ioctl(eth_hndl, ETH_CMD_G_MAC_ADDR, hello_msg.header.src.addr);
  195. while (1) {
  196. // Send the Hello frame
  197. ssize_t ret = write(eth_tap_fd, &hello_msg, ETH_HEADER_LEN + strlen(hello_msg.payload));
  198. if (ret == -1) {
  199. ESP_LOGE(TAG, "L2 TAP fd %d write error: errno: %d", eth_tap_fd, errno);
  200. break;
  201. }
  202. vTaskDelay(pdMS_TO_TICKS(3000));
  203. }
  204. close(eth_tap_fd);
  205. error:
  206. vTaskDelete(NULL);
  207. }
  208. void app_main(void)
  209. {
  210. // Initialize L2 TAP VFS interface
  211. ESP_ERROR_CHECK(esp_vfs_l2tap_intf_register(NULL));
  212. // Initialize NVS Flash
  213. ESP_ERROR_CHECK(nvs_flash_init());
  214. // Initialize TCP/IP network interface (should be called only once in application)
  215. ESP_ERROR_CHECK(esp_netif_init());
  216. ESP_ERROR_CHECK(esp_event_loop_create_default());
  217. /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
  218. * Read "Establishing Wi-Fi or Ethernet Connection" section in
  219. * examples/protocols/README.md for more information about this function.
  220. */
  221. ESP_ERROR_CHECK(example_connect());
  222. esp_eth_handle_t eth_hndl = get_example_eth_handle();
  223. uint8_t mac_addr[ETH_ADDR_LEN];
  224. esp_eth_ioctl(eth_hndl, ETH_CMD_G_MAC_ADDR, mac_addr);
  225. ESP_LOGI(TAG, "Ethernet HW Addr %02x:%02x:%02x:%02x:%02x:%02x",
  226. mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
  227. // Echo received message back to sender (blocks indefinitely)
  228. xTaskCreate(echo_l2tap_task, "echo", 4096, NULL, 6, NULL);
  229. // Echo received message back to sender (blocks with timeout)
  230. xTaskCreate(nonblock_l2tap_echo_task, "echo_no-block", 4096, NULL, 5, NULL);
  231. // Periodically broadcast "Hello message"
  232. xTaskCreate(hello_tx_l2tap_task, "hello_tx", 4096, NULL, 4, NULL);
  233. }