| /* |
| * |
| * Copyright (c) 2020 Project CHIP Authors |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <string.h> |
| #include <sys/param.h> |
| #include "freertos/FreeRTOS.h" |
| #include "freertos/task.h" |
| #include "esp_system.h" |
| #include "esp_wifi.h" |
| #include "esp_event.h" |
| #include "esp_log.h" |
| #include "nvs_flash.h" |
| #include "tcpip_adapter.h" |
| |
| #include "lwip/err.h" |
| #include "lwip/sockets.h" |
| #include "lwip/sys.h" |
| #include <lwip/netdb.h> |
| |
| #define PORT CONFIG_ECHO_PORT |
| #define RX_LEN 128 |
| #define ADDR_LEN 128 |
| |
| static const char * TAG = "echo server"; |
| |
| static void udp_server_task(void * pvParameters) |
| { |
| char rx_buffer[RX_LEN]; |
| char addr_str[ADDR_LEN]; |
| int addr_family = (int) pvParameters; |
| int ip_protocol = 0; |
| struct sockaddr_in6 dest_addr; |
| |
| while (1) |
| { |
| |
| if (addr_family == AF_INET) |
| { |
| struct sockaddr_in * dest_addr_ip4 = (struct sockaddr_in *) &dest_addr; |
| dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY); |
| dest_addr_ip4->sin_family = AF_INET; |
| dest_addr_ip4->sin_port = htons(PORT); |
| ip_protocol = IPPROTO_IP; |
| } |
| else if (addr_family == AF_INET6) |
| { |
| bzero(&dest_addr.sin6_addr.un, sizeof(dest_addr.sin6_addr.un)); |
| dest_addr.sin6_family = AF_INET6; |
| dest_addr.sin6_port = htons(PORT); |
| ip_protocol = IPPROTO_IPV6; |
| } |
| |
| int sock = socket(addr_family, SOCK_DGRAM, ip_protocol); |
| if (sock < 0) |
| { |
| ESP_LOGE(TAG, "Unable to create socket: errno %d", errno); |
| break; |
| } |
| ESP_LOGI(TAG, "Socket created"); |
| |
| #if defined(CONFIG_ECHO_IPV4) && defined(CONFIG_ECHO_IPV6) |
| if (addr_family == AF_INET6) |
| { |
| // Note that by default IPV6 binds to both protocols, it is must be disabled |
| // if both protocols used at the same time (used in CI) |
| int opt = 1; |
| setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); |
| setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)); |
| } |
| #endif |
| |
| int err = bind(sock, (struct sockaddr *) &dest_addr, sizeof(dest_addr)); |
| if (err < 0) |
| { |
| ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno); |
| // Avoid looping hard if binding fails continuously |
| vTaskDelay(50 / portTICK_PERIOD_MS); |
| continue; |
| } |
| ESP_LOGI(TAG, "Socket bound, port %d", PORT); |
| |
| while (1) |
| { |
| |
| ESP_LOGI(TAG, "Waiting for data"); |
| struct sockaddr_in6 source_addr; // Large enough for both IPv4 or IPv6 |
| socklen_t socklen = sizeof(source_addr); |
| int len = recvfrom(sock, rx_buffer, sizeof(rx_buffer) - 1, 0, (struct sockaddr *) &source_addr, &socklen); |
| |
| // Error occurred during receiving |
| if (len < 0) |
| { |
| ESP_LOGE(TAG, "recvfrom failed: errno %d", errno); |
| break; |
| } |
| // Data received |
| else |
| { |
| // Get the sender's ip address as string |
| if (source_addr.sin6_family == PF_INET) |
| { |
| inet_ntoa_r(((struct sockaddr_in *) &source_addr)->sin_addr.s_addr, addr_str, sizeof(addr_str) - 1); |
| } |
| else if (source_addr.sin6_family == PF_INET6) |
| { |
| inet6_ntoa_r(source_addr.sin6_addr, addr_str, sizeof(addr_str) - 1); |
| } |
| |
| rx_buffer[len] = 0; // Null-terminate whatever we received and treat like a string... |
| ESP_LOGI(TAG, "Received %d bytes from %s:", len, addr_str); |
| ESP_LOGI(TAG, "%s", rx_buffer); |
| |
| int err = sendto(sock, rx_buffer, len, 0, (struct sockaddr *) &source_addr, sizeof(source_addr)); |
| if (err < 0) |
| { |
| ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno); |
| break; |
| } |
| } |
| } |
| |
| if (sock != -1) |
| { |
| ESP_LOGE(TAG, "Shutting down socket and restarting..."); |
| shutdown(sock, 0); |
| close(sock); |
| } |
| } |
| vTaskDelete(NULL); |
| } |
| |
| // The echo server assumes the platform's networking has been setup already |
| void startServer(void) |
| { |
| #ifdef CONFIG_ECHO_IPV4 |
| xTaskCreate(udp_server_task, "udp_server", 4096, (void *) AF_INET, 5, NULL); |
| #endif |
| #ifdef CONFIG_ECHO_IPV6 |
| xTaskCreate(udp_server_task, "udp_server", 4096, (void *) AF_INET6, 5, NULL); |
| #endif |
| } |