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)
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
#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
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef UDP_PACKET_TYPE_H
|
||||
#define UDP_PACKET_TYPE_H
|
||||
|
||||
typedef enum {
|
||||
UDP_PACKET_TYPE_NONE = 0,
|
||||
UDP_PACKET_TYPE_PING = 1,
|
||||
UDP_PACKET_TYPE_PONG = 2,
|
||||
} udp_packet_type_t;
|
||||
|
||||
// Wire sizes in bytes
|
||||
#define UDP_PING_WIRE_SIZE 9 // 1 (type) + 8 (nonce)
|
||||
#define UDP_PONG_WIRE_SIZE 13 // 1 (type) + 8 (nonce) + 4 (proto_version)
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user