Files
dcrubro 21fe73fb01 Add NodeDiscovery: multi-hop peer crawl with UDP-ping preference
- Implement NodeDiscovery engine: known-peer table (DynArr) with a
  per-tick seed/ping/query/connect state machine driven by the node
  maintenance thread; bounded multi-hop crawl (FANOUT peers per node,
  hop-capped) that connects to reachable peers lowest-ping-first
- Add GET_PEERS/PEERS TCP opcodes for peer-list exchange, handled on
  both inbound and outbound connections
- Measure UDP round-trip time and pass it to the on_pong callback
  (previously the send timestamp was only used for retries)
- Advertise each node's listen port in HELLO/ACK_HELLO and store it
  per-connection, so inbound-only peers and non-default ports are
  discoverable (length-guarded parse; wire-compatible with old peers)
- Wire a udp_node_t + node_discovery_t into net_node_t: init/start in
  Node_Create, tick in the maintenance loop, teardown in Node_Destroy
  (stop UDP before destroying discovery to avoid callback races)
- Add Node_ConnListenEndpoint / Node_GetPeerEndpoints helpers to derive
  peers' listen endpoints (outbound: dialed port; inbound: advertised),
  with IPv4-mapped-IPv6 normalization and IP+port dedup
- Match ping pong/timeout callbacks by peer address (the UDP layer owns
  the nonce), fixing discovered peers stuck UNREACHABLE
- Ping the peer's listen port instead of the ephemeral TCP source port
  (fixes the original stub so pongs actually return)
- Add `peers` CLI command to dump the discovery table (endpoint/hop/
  state/ping)
- Add discovery tunables to constants.h (fanout, max hops, target
  connections, timeouts, caps)
2026-07-22 23:18:57 +02:00

57 lines
1.6 KiB
C

#ifndef UDP_NODE_H
#define UDP_NODE_H
#include <stdint.h>
#include <stdbool.h>
#include <pthread.h>
#include <netinet/in.h>
#include <udpd/udppackettype.h>
#define UDP_LISTEN_PORT 9393
#define UDP_PING_RETRY_INTERVAL_MS 1000
#define UDP_PING_MAX_RETRIES 3
#define UDP_MAX_PENDING_PINGS 64
typedef struct {
uint64_t nonce;
struct sockaddr_storage dest;
uint64_t lastSentMs;
int retries;
bool active;
} pending_ping_t;
typedef struct udp_node {
int sockFd; // AF_INET6, IPV6_V6ONLY=1
int sockFdV4; // AF_INET
volatile int isRunning;
pthread_t recvThreadV6;
pthread_t recvThreadV4;
pthread_t retryThread;
pending_ping_t pendingPings[UDP_MAX_PENDING_PINGS];
pthread_mutex_t pingsMutex;
void (*on_pong)(struct udp_node* node,
const struct sockaddr_storage* from,
uint64_t nonce, int protoVersion, uint64_t rttMs, void* user);
void (*on_ping_timeout)(struct udp_node* node,
const struct sockaddr_storage* dest,
uint64_t nonce, void* user);
void* callbackUser;
} udp_node_t;
int UdpNode_Init(udp_node_t* node, uint16_t port);
void UdpNode_SetCallbacks(udp_node_t* node,
void (*on_pong)(udp_node_t*, const struct sockaddr_storage*, uint64_t, int, uint64_t, void*),
void (*on_ping_timeout)(udp_node_t*, const struct sockaddr_storage*, uint64_t, void*),
void* user);
int UdpNode_Start(udp_node_t* node);
void UdpNode_Stop(udp_node_t* node);
void UdpNode_Destroy(udp_node_t* node);
int UdpNode_SendPing(udp_node_t* node, const struct sockaddr_storage* dest);
#endif