- u_async.c: process immediate_queue BEFORE epoll_wait in uasync_poll - stcp_link.c: defensive NULL etcp_conn/etcp_link in stcp_link_close - utun_instance.c: move stcp_server_list_destroy_all to Phase J (before ETCP), add deferred drain in both Phase J and L - stcp.h/c: STCP_HS_ENC_CLIENT 6→38, STCP_HS_ENC_SERVER 7→39, exchange ed25519_pubkey in both handshake directions - etcp_connections.c: copy peer_ed25519 from STCP to ETCP_CONN in etcp_link_enter_ready_tcp; guard link->etcp in tcp_link_close_cb - chat_sync.c: fix lk->conn NULL dereference for TCP links - test_stcp.c: update stcp_server_create/stcp_client_connect callsites - tools/chat_tcp_test: change transport udp→tcp
98 lines
5.6 KiB
Markdown
98 lines
5.6 KiB
Markdown
# Radix Tree (Patricia Trie) для IP-маршрутизации
|
|
|
|
## 1. Назначение
|
|
|
|
Бинарное radix-дерево (Patricia trie) для хранения маршрутов и поиска Longest Prefix Match (LPM). Производное от BSD `net/radix.c`. Используется в `route_lib.c` / `route6_lib.c` для таблиц маршрутизации IPv4/IPv6.
|
|
|
|
Каждый маршрут — пара `(key, mask)`. Ключи — sockaddr-подобные структуры: первый байт хранит длину всей структуры, остальное — IP-адрес. Максимальная длина ключа — 32 байта (`RADIX_MAX_KEY_LEN`).
|
|
|
|
Потокобезопасность: замки — заглушки (no-op). Подразумевается однопоточное использование с `u_async`.
|
|
|
|
## 2. Как пользоваться
|
|
|
|
```c
|
|
#include "../lib/radix.h"
|
|
|
|
// --- Инициализация ---
|
|
struct radix_node_head *rnh = NULL;
|
|
rn_inithead((void**)&rnh, 1); // off=1 пропускает байт длины в сравнениях
|
|
|
|
// --- Структура данных маршрута ---
|
|
struct route_data {
|
|
struct radix_node rn_nodes[2]; // ПАМЯТЬ ДЛЯ ДЕРЕВА — первые 2 поля!
|
|
uint8_t key[32];
|
|
uint8_t mask[32];
|
|
// ... пользовательские поля ...
|
|
int metric;
|
|
};
|
|
|
|
// --- Добавление маршрута ---
|
|
struct route_data *rd = u_calloc(sizeof(*rd), 1);
|
|
rd->key[0] = 17; // длина (1 + 16 байт IPv6)
|
|
memcpy(rd->key + 1, addr, 16);
|
|
rd->mask[0] = 17;
|
|
make_mask(rd->mask + 1, plen);
|
|
rn_addroute(rd->key, rd->mask, &rnh->rh, rd->rn_nodes);
|
|
|
|
// --- Longest Prefix Match ---
|
|
uint8_t search_key[17];
|
|
search_key[0] = 17;
|
|
memcpy(search_key + 1, dst_addr, 16);
|
|
struct radix_node *leaf = rn_match(search_key, &rnh->rh);
|
|
if (leaf && !(leaf->rn_flags & RNF_ROOT)) {
|
|
// leaf = &rd->rn_nodes[0] — восстанавливаем указатель на данные:
|
|
struct route_data *found = (struct route_data *)(
|
|
(char *)leaf - offsetof(struct route_data, rn_nodes));
|
|
}
|
|
|
|
// --- Точный поиск (key + mask) ---
|
|
struct radix_node *node = rn_lookup(key, mask, &rnh->rh);
|
|
|
|
// --- Удаление ---
|
|
rn_delete(rd->key, rd->mask, &rnh->rh);
|
|
|
|
// --- Обход всех маршрутов ---
|
|
static int walk_cb(struct radix_node *rn, void *arg) {
|
|
if (rn->rn_flags & RNF_ROOT) return 0;
|
|
struct route_data *rd = (struct route_data *)((char *)rn - offsetof(...));
|
|
// ... обработать rd ...
|
|
return 0; // 0 = продолжить, !=0 = прервать
|
|
}
|
|
rn_walktree(&rnh->rh, walk_cb, NULL);
|
|
|
|
// --- Обход с нижней границы (mask-фильтр) ---
|
|
uint8_t base_key[...], base_mask[...];
|
|
rn_walktree_from(&rnh->rh, base_key, base_mask, walk_cb, NULL);
|
|
|
|
// --- Завершение ---
|
|
rn_detachhead((void**)&rnh);
|
|
```
|
|
|
|
**Ключевые нюансы:**
|
|
- `rn_nodes[2]` **должны быть первыми полями** в структуре данных — дерево использует их для хранения.
|
|
- `rn_addroute` возвращает `&rd->rn_nodes[0]` при успехе. Для извлечения структуры используйте `offsetof()`.
|
|
- `rn_match` возвращает самый специфичный (longest prefix) лист либо `NULL`. Если `RNF_ROOT` — совпадений нет.
|
|
- Максимальный размер ключа — 32 байта (включая байт длины).
|
|
- Все динамические выделения через `R_Malloc`/`R_Zalloc`/`R_Free` (макросы на `u_malloc`/`u_free`).
|
|
- Потокобезопасность не реализована — замки (макросы `RADIX_NODE_HEAD_LOCK` и т.д.) пустые.
|
|
|
|
## 3. API
|
|
|
|
| Функция | Назначение |
|
|
|---------|-----------|
|
|
| `rn_inithead(void **head, int off)` | Выделяет и инициализирует `radix_node_head`. `off` — смещение (байт длины sockaddr). |
|
|
| `rn_detachhead(void **head)` | Освобождает дерево и все узлы (через `rn_walktree` + `rn_delete`). |
|
|
| `rn_match(key, head)` | Longest Prefix Match. Возвращает лист-победитель или NULL. |
|
|
| `rn_lookup(key, mask, head)` | Точный поиск (key + mask). Возвращает узел или NULL. |
|
|
| `rn_addroute(key, mask, head, nodes[2])` | Добавляет маршрут. `nodes` — память из структуры данных. |
|
|
| `rn_delete(key, mask, head)` | Удаляет маршрут. Возвращает удалённый узел для освобождения. |
|
|
| `rn_walktree(head, callback, arg)` | Обход всего дерева (in-order), вызывает `callback(rn, arg)` для каждого листа. |
|
|
| `rn_walktree_from(head, base_key, base_mask, callback, arg)` | Обход поддерева, ограниченного `base_mask`. |
|
|
| `rn_refines(mask1, mask2)` | Проверяет, является ли `mask1` более специфичной, чем `mask2`. |
|
|
| `rn_nextprefix(rn)` | Переход к следующему dupedkey-узлу с тем же ключом. |
|
|
|
|
**Структуры:**
|
|
- `struct radix_node_head` — корень дерева с таблицей функций (`rnh_matchaddr`, `rnh_addaddr`, ...).
|
|
- `struct radix_node` — узел: внутренний (bit-offset, left/right) или лист (key, mask, dupedkey).
|
|
- `struct radix_mask` — аннотация маски для нетривиальных (non-normal) масок в поддеревьях.
|