Browse Source

Fix: Add bounds/mtu checks, underflow protection and null deref fix for ETCP robustness

congestion
Evgeny 5 months ago
parent
commit
39544e6b2b
  1. 236
      lib/myip.c
  2. 25
      src/etcp.c
  3. 25
      src/etcp_connections.c
  4. 1
      src/etcp_connections.h
  5. 3
      src/etcp_loadbalancer.c
  6. 11
      src/route_p2pconn.txt
  7. 218
      utun.md

236
lib/myip.c

@ -0,0 +1,236 @@
/* outgoing_iface.c
*
* Определяет IPv4-адрес и индекс/имя исходящего интерфейса,
* который ОС выберет при отправке на заданный destination
* после bind на 0.0.0.0 (INADDR_ANY).
*
* Сборка:
* Linux / BSD: gcc -o out out.c
* Windows (MSVC): cl out.c /link ws2_32.lib iphlpapi.lib
* Windows (MinGW): gcc -o out.exe out.c -lws2_32 -liphlpapi
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
/* ===================== Платформенные заголовки ===================== */
#ifdef _WIN32
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include <winsock2.h>
# include <ws2tcpip.h>
# include <iphlpapi.h>
# pragma comment(lib, "ws2_32.lib")
# pragma comment(lib, "iphlpapi.lib")
typedef SOCKET socket_t;
# define SOCKET_INVALID INVALID_SOCKET
# define SOCKET_ERRNO WSAGetLastError()
/* На Windows нет IFNAMSIZ; берём с запасом */
# ifndef IF_NAMESIZE
# define IF_NAMESIZE 256
# endif
#else /* Linux, FreeBSD, OpenBSD, NetBSD, macOS, … */
# include <unistd.h>
# include <sys/types.h>
# include <sys/socket.h>
# include <netinet/in.h>
# include <arpa/inet.h>
# include <ifaddrs.h>
# include <net/if.h>
# include <errno.h>
typedef int socket_t;
# define SOCKET_INVALID (-1)
# define SOCKET_ERRNO errno
# ifndef IF_NAMESIZE
# define IF_NAMESIZE IFNAMSIZ /* обычно 16 */
# endif
#endif
/* ===================== Структура результата ===================== */
typedef struct {
char ip[INET_ADDRSTRLEN]; /* IP строкой, например "192.168.1.5" */
char name[IF_NAMESIZE]; /* имя интерфейса: "eth0", "em0", "Wi-Fi" … */
uint32_t ifindex; /* числовой ifindex (if_nametoindex) */
} iface_info_t;
/* ===================== Утилиты ===================== */
static int net_init(void)
{
#ifdef _WIN32
static int done = 0;
if (!done) {
WSADATA wsa;
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
return -1;
done = 1;
}
#endif
return 0;
}
static void socket_close(socket_t s)
{
#ifdef _WIN32
if (s != SOCKET_INVALID)
closesocket(s);
#else
if (s >= 0)
close(s);
#endif
}
/* ===================== Основная функция ===================== */
/*
* dst_ip — адрес назначения (например, "8.8.8.8" или IP вашего VPN-сервера)
* dst_port — любой открытый порт (например, 53 или 123), реальных пакетов не уйдёт
* info — заполняемая структура
*
* Возвращает 0 при успехе, -1 при ошибке.
*/
int get_outgoing_iface(const char *dst_ip, int dst_port, iface_info_t *info)
{
socket_t sock = SOCKET_INVALID;
int ret = -1;
struct sockaddr_in dst, src;
socklen_t src_len = sizeof(src);
memset(info, 0, sizeof(*info));
if (net_init() != 0)
return -1;
/* 1. UDP-сокет */
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock == SOCKET_INVALID)
return -1;
/* 2. Bind на 0.0.0.0 — именно так, как в вопросе */
memset(&dst, 0, sizeof(dst));
dst.sin_family = AF_INET;
dst.sin_addr.s_addr = INADDR_ANY; /* 0.0.0.0 */
dst.sin_port = 0; /* система выберет порт */
if (bind(sock, (struct sockaddr *)&dst, sizeof(dst)) != 0)
goto end;
/* 3. "Соединяемся" с destination.
* Для UDP это НЕ шлёт реальных пакетов, но ядро просчитывает
* маршрут и фиксирует выбранный source IP в сокете. */
memset(&dst, 0, sizeof(dst));
dst.sin_family = AF_INET;
dst.sin_port = htons((uint16_t)dst_port);
if (inet_pton(AF_INET, dst_ip, &dst.sin_addr) != 1)
goto end;
if (connect(sock, (struct sockaddr *)&dst, sizeof(dst)) != 0)
goto end;
/* 4. Забираем выбранный локальный адрес */
if (getsockname(sock, (struct sockaddr *)&src, &src_len) != 0)
goto end;
if (!inet_ntop(AF_INET, &src.sin_addr, info->ip, sizeof(info->ip)))
goto end;
/* 5. По source IP ищем интерфейс в системном списке */
#ifdef _WIN32
{
ULONG size = 0;
DWORD rv;
PIP_ADAPTER_ADDRESSES adapters = NULL, a;
PIP_ADAPTER_UNICAST_ADDRESS u;
/* первый вызов — узнать размер буфера */
rv = GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX,
NULL, NULL, &size);
if (rv != ERROR_BUFFER_OVERFLOW)
goto end;
adapters = (PIP_ADAPTER_ADDRESSES)malloc(size);
if (!adapters)
goto end;
rv = GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX,
NULL, adapters, &size);
if (rv != NO_ERROR) {
free(adapters);
goto end;
}
for (a = adapters; a != NULL; a = a->Next) {
for (u = a->FirstUnicastAddress; u; u = u->Next) {
struct sockaddr_in *ua =
(struct sockaddr_in *)u->Address.lpSockaddr;
if (ua->sin_addr.s_addr == src.sin_addr.s_addr) {
info->ifindex = (uint32_t)a->IfIndex;
/* FriendlyName — WCHAR, приводим к UTF-8 */
WideCharToMultiByte(CP_UTF8, 0, a->FriendlyName, -1,
info->name, IF_NAMESIZE,
NULL, NULL);
ret = 0;
goto win_done;
}
}
}
win_done:
free(adapters);
}
#else /* Linux / BSD / macOS */
{
struct ifaddrs *ifap, *ifa;
if (getifaddrs(&ifap) != 0)
goto end;
for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
if (!ifa->ifa_addr || ifa->ifa_addr->sa_family != AF_INET)
continue;
struct sockaddr_in *addr = (struct sockaddr_in *)ifa->ifa_addr;
if (addr->sin_addr.s_addr == src.sin_addr.s_addr) {
strncpy(info->name, ifa->ifa_name, IF_NAMESIZE - 1);
info->ifindex = if_nametoindex(ifa->ifa_name);
ret = 0;
break;
}
}
freeifaddrs(ifap);
}
#endif
end:
socket_close(sock);
return ret;
}
/* ===================== Пример использования =====================
int main(int argc, char **argv)
{
iface_info_t info;
const char *dst = (argc > 1) ? argv[1] : "8.8.8.8";
int port = (argc > 2) ? atoi(argv[2]) : 53;
if (get_outgoing_iface(dst, port, &info) == 0) {
printf("Destination : %s:%d\n", dst, port);
printf("Source IP : %s\n", info.ip);
printf("Iface name : %s\n", info.name);
printf("Iface index : %u\n", info.ifindex);
return 0;
}
fprintf(stderr, "Failed to determine outgoing interface (errno=%d)\n",
#ifdef _WIN32
(int)WSAGetLastError()
#else
errno
#endif
);
return 1;
}
*/

25
src/etcp.c

@ -1021,7 +1021,11 @@ void etcp_ack_recv(struct ETCP_CONN* etcp, uint32_t seq, uint16_t ts, uint16_t d
// Update connection statistics
etcp->unacked_bytes -= acked_pkt->ll.len;
if (etcp->unacked_bytes >= acked_pkt->ll.len) etcp->unacked_bytes -= acked_pkt->ll.len;
else {
DEBUG_WARN(DEBUG_CATEGORY_ETCP, "[%s] unacked_bytes underflow prevented: %u < %u", etcp->log_name, etcp->unacked_bytes, acked_pkt->ll.len);
etcp->unacked_bytes = 0;
}
etcp->bytes_sent_total += acked_pkt->ll.len;
etcp->ack_packets_count++;
@ -1063,6 +1067,7 @@ void etcp_conn_input(struct ETCP_DGRAM* pkt) {
// Process sections as per protocol.txt
switch (type) {
case ETCP_SECTION_ACK: {
if (len < 2) { len = 0; break; }
int elm_cnt=data[1];
uint32_t till=data[2] | (data[3]<<8) | (data[4]<<16) | (data[5]<<24);
uint16_t rx_dup_count_16 = data[6] | (data[7]<<8);
@ -1070,6 +1075,11 @@ void etcp_conn_input(struct ETCP_DGRAM* pkt) {
int16_t diff = (int16_t)(rx_dup_count_16 - old_tx_dup_16);
etcp->tx_dup_count += diff;
int ack_section_len = 8 + elm_cnt * 8;
if (ack_section_len > len) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[%s] ACK section too large: elm_cnt=%d len=%d", etcp->log_name, elm_cnt, len);
len = 0;
break;
}
data+=ack_section_len;
len-=ack_section_len;
for (int i=0; i<elm_cnt; i++) {
@ -1082,6 +1092,7 @@ void etcp_conn_input(struct ETCP_DGRAM* pkt) {
break;
}
case ETCP_SECTION_TIMESTAMP: {
if (len < 5) { len = 0; break; }
uint16_t cur_ts=get_current_timestamp();
uint16_t ret_ts=data[1] | (data[2]<<8);// cur_ts=ret_ts = RTT
uint16_t new_rtt=cur_ts-ret_ts;
@ -1185,6 +1196,11 @@ void etcp_conn_input(struct ETCP_DGRAM* pkt) {
}
}
struct ACK_PACKET* p = (struct ACK_PACKET*)queue_entry_new_from_pool(etcp->instance->ack_pool);
if (!p) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[%s] failed to allocate ACK_PACKET", etcp->log_name);
len = 0;
break;
}
p->seq=seq;
p->pkt_timestamp=pkt->timestamp;
p->recv_timestamp=get_current_timestamp();
@ -1218,6 +1234,13 @@ void etcp_conn_input(struct ETCP_DGRAM* pkt) {
rx_pkt->ll.len = pkt_len;
rx_pkt->ll.dgram_pool = etcp->instance->data_pool;
rx_pkt->ll.memlen = etcp->instance->data_pool->object_size;
if (pkt_len > (int)etcp->instance->data_pool->object_size) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[%s] payload too large for data_pool: %u > %zu", etcp->log_name, pkt_len, etcp->instance->data_pool->object_size);
memory_pool_free(etcp->instance->data_pool, payload_data);
queue_entry_free(&rx_pkt->ll);
len = 0;
break;
}
// Copy the actual payload data
memcpy(payload_data, data + 5, pkt_len);
queue_data_put_with_index(etcp->recv_q, (struct ll_entry*)rx_pkt, 0, 4);

25
src/etcp_connections.c

@ -108,6 +108,11 @@ static void etcp_link_send_init(struct ETCP_LINK* link, uint8_t reset) {
int to_add=s-offset-UDP_HDR_SIZE - UDP_SC_HDR_SIZE;
if (to_add<0) to_add=0;
int max_data = PACKET_DATA_SIZE - (int)sizeof(struct ETCP_DGRAM);
if (offset + to_add + SC_PUBKEY_ENC_SIZE > max_data) {
to_add = max_data - offset - SC_PUBKEY_ENC_SIZE;
if (to_add<0) to_add=0;
}
for (int i=0; i<to_add; i++) dgram->data[offset++]=rand();// fill pad
// padding end
@ -457,6 +462,7 @@ struct ETCP_SOCKET* etcp_socket_add(struct UTUN_INSTANCE* instance, struct CFG_S
int fib = server->fib;
uint8_t type = server->type;
int mtu = server->mtu ? server->mtu : instance->config->global.mtu;
if (mtu == 0 || mtu > PACKET_DATA_MAX_MTU) mtu = PACKET_DATA_MAX_MTU;
int loss_rate = server->loss_rate;
uint8_t only_local = server->only_local;
char* name = server->name;
@ -696,6 +702,7 @@ struct ETCP_LINK* etcp_link_new(struct ETCP_CONN* etcp, struct ETCP_SOCKET* conn
link->is_server = is_server;
int mtu = conn->mtu;
if (mtu == 0) mtu = 1500;
if (mtu > PACKET_DATA_MAX_MTU) mtu = PACKET_DATA_MAX_MTU;
link->mtu_local = mtu;
link->mtu = mtu;
link->initialized = 0;
@ -1234,6 +1241,11 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
errorcode=7;
goto ec_fr;
}
if (pkt_len<12) {
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "decrypted packet too short for INIT header, pkt_len=%zu from %s", pkt_len, sockaddr_storage_to_str(&addr).str);
errorcode=7;
goto ec_fr;
}
pkt->data_len=pkt_len-3;
pkt->noencrypt_len=0;
@ -1252,6 +1264,11 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
} *ack_hdr=(void*)&pkt->data[0];
uint64_t peer_id = be64toh(*(uint64_t*)ack_hdr->id);
if (ack_hdr->code == ETCP_PING) {
if (pkt_len < 22) {
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "PING too short: pkt_len=%zu from %s", pkt_len, sockaddr_storage_to_str(&addr).str);
errorcode=7;
goto ec_fr;
}
uint64_t nonce = be64toh(*(uint64_t*)(pkt->data + 9));
uint16_t ulen = be16toh(*(uint16_t*)(pkt->data + 17));
const uint8_t* udata = (ulen > 0) ? (pkt->data + 19) : NULL;
@ -1289,6 +1306,11 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
return;
}
if (ack_hdr->code == ETCP_PONG) {
if (pkt_len < 20) {
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "PONG too short: pkt_len=%zu from %s", pkt_len, sockaddr_storage_to_str(&addr).str);
errorcode=7;
goto ec_fr;
}
uint64_t nonce = be64toh(*(uint64_t*)(pkt->data + 9));
uint16_t ulen = 0;
const uint8_t* udata = NULL;
@ -1466,6 +1488,7 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
}
link->mtu_remote = (ack_hdr->mtu[0] << 8) | ack_hdr->mtu[1];
if (link->mtu_remote > PACKET_DATA_MAX_MTU) link->mtu_remote = PACKET_DATA_MAX_MTU;
link->mtu = link->mtu_local < link->mtu_remote ? link->mtu_local : link->mtu_remote;
struct {
@ -1526,6 +1549,7 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
int to_add=s - xoffset - UDP_HDR_SIZE - UDP_SC_HDR_SIZE;
if (to_add<0) to_add=0;
if (xoffset + to_add > PACKET_DATA_SIZE) { to_add = PACKET_DATA_SIZE - xoffset; if (to_add<0) to_add=0; }
for (int i=0; i<to_add; i++) pkt->data[xoffset++]=rand();// fill pad
// padding end
@ -1605,6 +1629,7 @@ process_decrypted:
return;
}
link->mtu_remote = (pkt->data[offset++] << 8) | pkt->data[offset++];
if (link->mtu_remote > PACKET_DATA_MAX_MTU) link->mtu_remote = PACKET_DATA_MAX_MTU;
link->mtu = link->mtu_local < link->mtu_remote ? link->mtu_local : link->mtu_remote;
link->remote_link_id = pkt->data[offset++];
link->remote_socket_id = pkt->data[offset++];

1
src/etcp_connections.h

@ -15,6 +15,7 @@
#define ACK_REZERV 100// сколько байт резервировать под ack и прочие заголовки
#define PACKET_DATA_SIZE 1600//1536
#define PACKET_DATA_MAX_MTU 1600
// Типы кодограмм протокола
#define ETCP_INIT_REQUEST 0x02

3
src/etcp_loadbalancer.c

@ -114,8 +114,7 @@ void etcp_loadbalancer_send(struct ETCP_DGRAM* dgram) {
struct ETCP_CONN* etcp = dgram->link ? dgram->link->etcp : NULL;
if (!etcp) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[%s] no ETCP_CONN associated with dgram", etcp->log_name);
memory_pool_free(etcp->instance->pkt_pool, dgram);
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "no ETCP_CONN associated with dgram (link=%p)", (void*)dgram->link);
return;
}

11
src/route_p2pconn.txt

@ -0,0 +1,11 @@
Установка прямого подключения между узлами
1. узел собирает адреса - кандидаты для подключения:
список пар адрес-порт:
- адреса и порты из конфига сокетов (если не 0.0.0.0)
- адреса и порты из сокетов (nat) где nat = EIM
список ретрансляторов узлов-кандидатов:
- rtt, nodeid, адрес-порт
пары ip:port передаются на peer в команде "direct connection request" ROUTE_SUBCMD_CONN_REQ 0x0B

218
utun.md

@ -0,0 +1,218 @@
# uTun — защищённый VPN-туннель поверх UDP
uTun создаёт виртуальный TUN-интерфейс и маршрутизирует IP-трафик через зашифрованные UDP-каналы по протоколу **ETCP** (Extended TCP) — комбинация TCP-надёжности + QUIC-мультиплексирования + multi-link балансировки.
## Возможности
| Возможность | Описание |
|---|---|
| **Шифрование** | ECC (secp256r1) обмен ключами + AES-128-CCM с аутентификацией |
| **Целостность** | CRC32 внутри зашифрованного payload |
| **Multi-link** | Одно подключение через несколько UDP-каналов одновременно |
| **Балансировка** | Выбор канала с минимальным inflight + round-robin при равенстве |
| **Bandwidth shaping** | Ограничение полосы на каждом канале с автооценкой |
| **Inflight control** | Динамическое ограничение байт в полёте для предотвращения перегрузки |
| **Retransmission** | Адаптивный таймаут: RTT_avg × k₁ + jitter × k₂ |
| **Keepalive** | Двусторонний, с настраиваемым интервалом и таймаутом |
| **Фрагментация/сборка** | Normalizer: упаковка мелких пакетов вместе, разбиение крупных |
| **Маршрутизация** | Локальная (через TUN + системные маршруты) и распределённая (BGP-обмен) |
| **NAT-детекция** | Определение типа NAT (open/restricted) через ping-пробы |
| **Файрвол** | Белый список IP:port для трафика через туннель |
| **Control server** | TCP-сервер для мониторинга метрик в реальном времени (GUI-клиент) |
| **Горячая перезагрузка** | SIGHUP — выборочное обновление конфига (сравнение сокетов/клиентов/линков) |
| **Авто-ключи** | При первом запуске генерирует ECC-пару и node_id, записывает в конфиг |
| **Демонизация** | fork/setsid на Linux; foreground-режим `-f` для отладки |
| **Кроссплатформенность** | Linux (epoll), Windows (wintun), FreeBSD |
| **Эмуляция потерь** | `loss_rate=N` в конфиге сокета — для тестирования |
## Архитектура (кратко)
```
Приложение/ОС
↓ IP-пакеты
TUN interface (tun_if)
↓ queue
Routing (routing.c) — поиск маршрута
↓ (если удалённый)
Normalizer (pkt_normalizer.c) — фрагментация/упаковка
↓
ETCP (etcp.c) — seq/ack, retransmit, reassembly
↓
LoadBalancer (etcp_loadbalancer.c) — выбор канала
↓
Encrypt + UDP send (etcp_connections.c)
↓
Сеть (UDP)
```
## Конфигурация (INI-формат)
### CLI-запуск
```
utun -c utun.conf [-p /var/run/utun.pid] [-l utun.log] [-f] [-d "etcp:debug"]
```
- `-c` — путь к конфигу (по умолчанию `utun.conf`)
- `-p` — PID-файл
- `-l` — лог-файл (по умолчанию `utun.log`)
- `-f` — foreground (не демонизироваться)
- `-d` — отладочный конфиг на лету, формат: `"категория:уровень,..."`
### Секции конфига
#### `[global]` — основные параметры
| Ключ | Значение | По умолчанию |
|---|---|---|
| `my_node_name` | Имя узла (до 15 символов) | — |
| `my_node_id` | 64-битный ID узла (hex, 16 символов) | авто |
| `my_private_key` | Приватный ключ ECC (64 hex символа) | авто |
| `my_public_key` | Публичный ключ ECC (128 hex символов) | авто |
| `tun_ifname` | Имя TUN-интерфейса | `tun0` |
| `tun_ip` | IP-адрес TUN-интерфейса | — |
| `mtu` | MTU для всех подключений | `1500` |
| `keepalive_timeout` | Таймаут keepalive (мс) | `2000` |
| `keepalive_interval` | Интервал keepalive (мс) | `200` |
| `log_file` | Путь к лог-файлу | — |
| `debug_level` | Уровень отладки: `error`, `warn`, `info`, `debug`, `trace` | `error` |
| `tun_test_mode` | 1 — не создавать реальный TUN (тесты) | `0` |
| `enable_timestamp` | Метки времени в логах | `1` |
| `enable_function_names` | Имена функций в логах | `1` |
| `enable_file_lines` | Файл:строка в логах | `0` |
| `enable_colors` | ANSI-цвета в терминале | `0` |
#### `[debug]` — по-категорийные уровни (переопределяют `debug_level`)
Доступные категории:
`uasync`, `ll_queue`, `connection`, `etcp`, `crypto`, `memory`, `timing`, `config`, `tun`, `routing`, `timers`, `normalizer`, `bgp`, `socket`, `general`, `control`, `dump`, `traffic`
Пример:
```ini
[debug]
etcp=info
routing=trace
bgp=trace
crypto=error
```
#### `[server: ИМЯ]` — локальные UDP-сокеты (обязательно хотя бы один)
| Ключ | Значение | По умолчанию |
|---|---|---|
| `addr` | `IP:port` для bind | **обязателен** |
| `type` | `public`, `nat`, `private` | `unknown` |
| `mtu` | MTU для этого сокета | из `[global]` |
| `loss_rate` | % потерь пакетов (0–100) | `0` |
| `so_mark` | Linux SO_MARK для policy routing | — |
| `netif` | Привязка к интерфейсу (по имени) | — |
| `fib` | FreeBSD FIB (таблица маршрутизации) | `-1` (не задан) |
| `only_local` | 1 — только локальные соединения, без forwarding | `0` |
Пример:
```ini
[server: lan1]
addr=192.168.29.117:1333
type=public
```
#### `[client: ИМЯ]` — исходящие подключения к пиру
| Ключ | Значение | По умолчанию |
|---|---|---|
| `link` | `сервер:удалённый_IP:порт` (можно несколько) | **обязателен** |
| `peer_public_key` | Публичный ключ пира (128 hex) | **обязателен** |
| `keepalive` | 1 — включить keepalive | `1` |
Пример:
```ini
[client: aeza]
link=lan1:85.192.42.96:1333
peer_public_key=04c1cae041a8e6bfba5245f6669c73f0793d7f9929300a2ba2e123ca55260d6e47...
```
> **Важно:** в серверном конфиге секции `[client]` нет. В клиентском — есть свои ключи + `peer_public_key` каждого сервера в `[client]`.
#### `[routing]` — маршруты
| Ключ | Значение |
|---|---|
| `route_subnet` | Подсеть, доступная через этот TUN (добавляется в системную таблицу маршрутизации). Можно несколько |
| `my_subnet` | Своя подсеть, анонсируемая через BGP |
Пример:
```ini
[routing]
route_subnet=10.23.0.0/16
route_subnet=10.24.0.0/16
my_subnet=10.23.1.0/24
```
#### `[firewall]` — контроль доступа
| Ключ | Значение |
|---|---|
| `allow` | `all` — разрешить всё. Или `IP` / `IP:port` (можно несколько) |
По умолчанию: deny all (если нет allow=all).
Пример:
```ini
[firewall]
allow=192.168.1.100
allow=10.0.0.0/8:443
```
#### `[control]` — сервер мониторинга
| Ключ | Значение | По умолчанию |
|---|---|---|
| `control_ip` | IP для прослушивания | `127.0.0.1` |
| `control_port` | Порт | — |
| `control_allow` | `IP/mask` (можно несколько) | deny all |
Пример:
```ini
[control]
control_ip=192.168.29.117
control_port=9090
control_allow=192.168.0.0/16
```
#### `[allowed_keys]` — белый список публичных ключей пиров
| Ключ | Значение | По умолчанию |
|---|---|---|
| `allow_all` | 1 — разрешить все ключи | — |
| `key` | Публичный ключ (128 hex, можно несколько) | — |
Поведение:
- Секция отсутствует: разрешить все (совместимость)
- `allow_all=0` + нет ключей: запретить все
- `allow_all=0` + ключи: только перечисленные ключи
Пример:
```ini
[allowed_keys]
key=c6c8a8a9616ffa6cf44d4757e2bc9f7bd78a1d3cbbe75cffaf3cab8885ad48e7677b...
key=04c1cae041a8e6bfba5245f6669c73f0793d7f9929300a2ba2e123ca55260d6e4748...
```
### Пример минимального конфига
```ini
[global]
tun_ip=10.23.1.1
my_node_name=vmL1
my_node_id=5f75c7445af88e1f
my_private_key=b0e8b68679db468979906894fe36239dbda910e06435361d696c7f5a8e2009c4
my_public_key=c6c8a8a9616ffa6cf44d4757e2bc9f7bd78a1d3cbbe75cffaf3cab8885ad48e7677b...
[server: lan1]
addr=192.168.29.117:1333
type=public
[routing]
route_subnet=10.23.0.0/16
my_subnet=10.23.1.0/24
```
Loading…
Cancel
Save