blob: 8775b7b1902913b440e06a05f837e5704e22325a [file] [log] [blame]
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <platform/ESP32/route_hook/ESP32RouteTable.h>
#include <string.h>
#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;
}
}