/* * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include "esp_err.h" #include "esp_log.h" #include "lwip/ip6_addr.h" #include "lwip/netif.h" #include "lwip/timeouts.h" #define LWIP_MAX_TIMEOUT_SECONDS LWIP_UINT32_MAX / (1000 * 4) // lwIP defined reasonable timeout value static esp_route_entry_t * s_route_entries = NULL; static esp_route_entry_t * find_route_entry(const esp_route_entry_t * route_entry) { for (esp_route_entry_t * iter = s_route_entries; iter != NULL; iter = iter->next) { if (iter->netif == route_entry->netif && iter->prefix_length == route_entry->prefix_length && memcmp(iter->gateway.addr, route_entry->gateway.addr, sizeof(route_entry->gateway.addr)) == 0 && memcmp(iter->prefix.addr, route_entry->prefix.addr, route_entry->prefix_length / 8) == 0) { return iter; } } return NULL; } static esp_err_t remove_route_entry(esp_route_entry_t * route_entry) { if (s_route_entries == route_entry) { s_route_entries = s_route_entries->next; free(route_entry); return ESP_OK; } for (esp_route_entry_t * iter = s_route_entries; iter != NULL; iter = iter->next) { if (iter->next == route_entry) { iter->next = route_entry->next; free(route_entry); return ESP_OK; } } ESP_LOGW(TAG, "The given route entry is not found"); return ESP_ERR_NOT_FOUND; } static void route_timeout_handler(void * arg) { esp_route_entry_t * route = (esp_route_entry_t *) arg; if (route->lifetime_seconds <= LWIP_MAX_TIMEOUT_SECONDS) { remove_route_entry(route); } else { route->lifetime_seconds -= LWIP_MAX_TIMEOUT_SECONDS; sys_timeout(route->lifetime_seconds <= LWIP_MAX_TIMEOUT_SECONDS ? route->lifetime_seconds * 1000 : LWIP_MAX_TIMEOUT_SECONDS * 1000, route_timeout_handler, route); } } esp_route_entry_t * esp_route_table_add_route_entry(const esp_route_entry_t * route_entry) { if (route_entry == NULL) { return NULL; } esp_route_entry_t * entry = find_route_entry(route_entry); if (entry == NULL) { entry = (esp_route_entry_t *) malloc(sizeof(esp_route_entry_t)); if (entry == NULL) { ESP_LOGW(TAG, "Cannot allocate route entry"); return NULL; } entry->netif = route_entry->netif; entry->gateway = route_entry->gateway; ip6_addr_assign_zone(&entry->gateway, IP6_UNICAST, entry->netif); entry->prefix = route_entry->prefix; entry->prefix_length = route_entry->prefix_length; entry->next = s_route_entries; s_route_entries = entry; } else { sys_untimeout(route_timeout_handler, entry); } entry->preference = route_entry->preference; entry->lifetime_seconds = route_entry->lifetime_seconds; if (entry->lifetime_seconds != LWIP_UINT32_MAX) { sys_timeout(entry->lifetime_seconds <= LWIP_MAX_TIMEOUT_SECONDS ? entry->lifetime_seconds * 1000 : LWIP_MAX_TIMEOUT_SECONDS * 1000, route_timeout_handler, entry); } return entry; } static inline bool is_better_route(const esp_route_entry_t * lhs, const esp_route_entry_t * rhs) { if (rhs == NULL) { return true; } if (lhs == NULL) { return false; } return (lhs->prefix_length > rhs->prefix_length) || (lhs->prefix_length == rhs->prefix_length && lhs->preference > rhs->preference); } static inline bool route_match(const esp_route_entry_t * route, const ip6_addr_t * dest) { return memcmp(dest, route->prefix.addr, route->prefix_length / 8) == 0; } struct netif * lwip_hook_ip6_route(const ip6_addr_t * src, const ip6_addr_t * dest) { esp_route_entry_t * route = NULL; for (esp_route_entry_t * iter = s_route_entries; iter != NULL; iter = iter->next) { if (route_match(iter, dest) && is_better_route(iter, route)) { route = iter; } } if (route) { return route->netif; } else { return NULL; } } const ip6_addr_t * lwip_hook_nd6_get_gw(struct netif * netif, const ip6_addr_t * dest) { esp_route_entry_t * route = NULL; for (esp_route_entry_t * iter = s_route_entries; iter != NULL; iter = iter->next) { if (iter->netif == netif && route_match(iter, dest) && is_better_route(iter, route)) { route = iter; } } if (route) { return &route->gateway; } else { return NULL; } }