Distinguish LAN nonce failures from ordinary unknown devices

This commit is contained in:
evgeny committed 2026-10-25 17:55:35 +01:00
1 parent 94a76e569e
commit 8dd28fb90a
3 files changed
+18 -9

No files matched your search

+1 -1
View File
@@ -121,7 +121,7 @@ static int route_verify(void* arg, uint64_t peer, const uint8_t* data, size_t le
struct TOPO_NODE* node = topo_node_registry_find(inst->topo_groups, peer);
if (node && node->timestamp) memcpy(key, node->ed25519_public_key, sizeof(key));
else if (!inst->topo_sqlite_db || topo_node_sqlite_get_ed25519_pubkey(inst->topo_sqlite_db, peer, key)) {
DEBUG_DEBUG(DEBUG_CATEGORY_DISCOVERY, "LAN route ignored peer=%016llx reason=unknown signing key", (unsigned long long)peer); return -1;
DEBUG_DEBUG(DEBUG_CATEGORY_DISCOVERY, "LAN route ignored peer=%016llx reason=unknown signing key", (unsigned long long)peer); return 1;
}
if (sc_ed25519_verify(key, data, len, signature) != SC_OK) {
DEBUG_WARN(DEBUG_CATEGORY_DISCOVERY, "LAN route signature rejected peer=%016llx", (unsigned long long)peer); return -1;
+15 -7
View File
@@ -19,7 +19,7 @@
enum packet_drop {
DROP_FOREIGN, DROP_NONLOCAL, DROP_HEADER, DROP_ANNOUNCE, DROP_IDENTITY,
DROP_ENDPOINT, DROP_ACK, DROP_MESSAGE, DROP_CAPACITY, DROP_LIMIT, DROP_COUNT
DROP_ENDPOINT, DROP_ACK, DROP_MESSAGE, DROP_CAPACITY, DROP_LIMIT, DROP_ROUTE_NONCE, DROP_ROUTE_ADDRESS, DROP_COUNT
};
struct local_socket {
@@ -98,7 +98,8 @@ static void drop_packet(struct local_transport* t, enum packet_drop reason) { t-
static void reset_receive(struct local_transport* t) {
static const char* reasons[DROP_COUNT] = {
"foreign", "non-local source", "header/discovery type", "announcement/name", "identity/destination",
"unknown/expired endpoint", "ACK payload", "message/application limit", "table capacity", "receive budget"
"unknown/expired endpoint", "ACK payload", "message/application limit", "table capacity", "receive budget",
"LAN proof nonce/epoch/destination", "LAN signed/source IP mismatch"
};
for (int i = 0; i < DROP_COUNT; i++) {
if (t->dropped[i]) {
@@ -273,13 +274,16 @@ static void receive_route(struct local_socket* s, const struct sockaddr_storage*
if (destination || destination_epoch || memcmp(body, zero, 16)) { drop_packet(t, DROP_IDENTITY); return; }
} else if (destination != t->node_id || destination_epoch != t->epoch ||
!e->challenge_until || now >= e->challenge_until || epoch != e->route_epoch || memcmp(body, e->nonce, 16)) {
drop_packet(t, DROP_IDENTITY); return;
DEBUG_DEBUG(DEBUG_CATEGORY_DISCOVERY, "LAN proof rejected peer=%016llx epoch=%016llx expected=%016llx pending=%d expired=%d",
(unsigned long long)sender, (unsigned long long)epoch, (unsigned long long)e->route_epoch,
e->challenge_until != 0, e->challenge_until != 0 && now >= e->challenge_until);
drop_packet(t, DROP_ROUTE_NONCE); return;
}
const void* ip = source->ss_family == AF_INET ? (const void*)&((const struct sockaddr_in*)source)->sin_addr :
(const void*)&((const struct sockaddr_in6*)source)->sin6_addr;
size_t ip_size = source->ss_family == AF_INET ? 4 : 16;
if (body[16] != (ip_size == 4 ? 4 : 6) || memcmp(body + 17, ip, ip_size) ||
(ip_size == 4 && memcmp(body + 21, zero, 12))) { drop_packet(t, DROP_NONLOCAL); return; }
(ip_size == 4 && memcmp(body + 21, zero, 12))) { drop_packet(t, DROP_ROUTE_ADDRESS); return; }
struct local_route_socket sockets[LOCAL_ROUTE_SOCKETS_MAX] = {0};
for (unsigned i = 0; i < body[33]; i++) {
const uint8_t* p = body + LOCAL_ROUTE_HEADER_SIZE + i * LOCAL_ROUTE_SOCKET_SIZE;
@@ -300,8 +304,10 @@ static void receive_route(struct local_socket* s, const struct sockaddr_storage*
if (!t->routes_left) { drop_packet(t, DROP_LIMIT); return; }
t->routes_left--;
size_t signed_size = size - LOCAL_ROUTE_SIGNATURE_SIZE;
if (t->callbacks.route_verify(t->arg, sender, data, signed_size, data + signed_size)) {
drop_packet(t, DROP_IDENTITY); return;
int verified = t->callbacks.route_verify(t->arg, sender, data, signed_size, data + signed_size);
if (verified) {
if (verified < 0) drop_packet(t, DROP_IDENTITY);
return;
}
if (type == LOCAL_ROUTE_ANNOUNCE) {
int equal = e->route_count == body[33] && e->route_epoch == epoch;
@@ -363,8 +369,10 @@ static void receive_packet(struct local_socket* s, struct sockaddr_storage* sour
p = &t->peers[t->peer_count++]; memset(p, 0, sizeof(*p)); p->peer.node_id = peer_id;
}
if (p->epoch != epoch) {
int had_routes = 0;
for (size_t i = 0; i < p->count; i++) if (p->endpoints[i].route_until) had_routes = 1;
p->epoch = epoch; p->count = 0; changed = 1;
if (t->callbacks.routes_changed) t->callbacks.routes_changed(t->arg, peer_id);
if (had_routes && t->callbacks.routes_changed) t->callbacks.routes_changed(t->arg, peer_id);
}
if (strlen(p->peer.name) != len || memcmp(p->peer.name, payload, len)) {
memcpy(p->peer.name, payload, len); p->peer.name[len] = 0; changed = 1;
+2 -1
View File
@@ -55,7 +55,8 @@ struct local_transport_callbacks {
void (*delivery)(void* arg, uint64_t request_id, uint64_t peer_id, int status); // один результат: enum local_delivery_status
void (*activity_changed)(void* arg, int active); // сокеты включаются/выключаются, в том числе при ошибке таймера
/* Необязательный LAN transport: задать все пять callbacks либо ни одного.
* sockets возвращает 0..capacity или -1; sign/verify — 0/-1, подпись Ed25519 ровно 64 байта.
* sockets возвращает 0..capacity или -1; sign — 0/-1; verify — 0 (верна), 1 (ключ неизвестен), -1 (неверна/ошибка).
* Подпись Ed25519 ровно 64 байта.
* Подписываются полный wire-заголовок и тело без подписи; verify использует только известный ключ peer.
* offline возвращает 1 при отсутствии прямого UP и живого BGP-пути во всех группах.
* routes_changed вызывается после применения/удаления адресов; не останавливать транспорт из callback. */