Discovery: reply unicast when responding to new/restarted peer

When we see a new peer or detect a restart (site_id change for known addr+port),
send the announcement directly to that host via unicast instead of broadcasting
to the multicast group. This avoids waking every other node on the subnet for a
reply that is only relevant to one machine.

The periodic multicast announcements continue unchanged for initial discovery.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-30 04:31:56 +00:00
parent 780f45b46f
commit a5198e84d2

View File

@@ -76,19 +76,14 @@ static int find_slot(struct Discovery *d) {
/* -- send ------------------------------------------------------------------ */ /* -- send ------------------------------------------------------------------ */
static void send_announcement(struct Discovery *d) { static size_t build_announcement(struct Discovery *d, uint8_t *buf) {
size_t name_len = strlen(d->config.name); size_t name_len = strlen(d->config.name);
if (name_len > DISCOVERY_MAX_NAME_LEN) { name_len = DISCOVERY_MAX_NAME_LEN; } if (name_len > DISCOVERY_MAX_NAME_LEN) { name_len = DISCOVERY_MAX_NAME_LEN; }
uint32_t payload_len = (uint32_t)(ANN_FIXED_SIZE + name_len); uint32_t payload_len = (uint32_t)(ANN_FIXED_SIZE + name_len);
size_t total = TRANSPORT_FRAME_HEADER_SIZE + payload_len; put_u16(buf, 0, 0x0010);
uint8_t buf[TRANSPORT_FRAME_HEADER_SIZE + ANN_FIXED_SIZE + DISCOVERY_MAX_NAME_LEN];
/* frame header */
put_u16(buf, 0, 0x0010); /* message_type: DISCOVERY_ANNOUNCE */
put_u32(buf, 2, payload_len); put_u32(buf, 2, payload_len);
/* announcement payload */
uint8_t *p = buf + TRANSPORT_FRAME_HEADER_SIZE; uint8_t *p = buf + TRANSPORT_FRAME_HEADER_SIZE;
put_u8 (p, ANN_PROTOCOL_VERSION, DISCOVERY_PROTOCOL_VERSION); put_u8 (p, ANN_PROTOCOL_VERSION, DISCOVERY_PROTOCOL_VERSION);
put_u16(p, ANN_SITE_ID, d->config.site_id); put_u16(p, ANN_SITE_ID, d->config.site_id);
@@ -97,10 +92,26 @@ static void send_announcement(struct Discovery *d) {
put_u8 (p, ANN_NAME_LEN, (uint8_t)name_len); put_u8 (p, ANN_NAME_LEN, (uint8_t)name_len);
memcpy(p + ANN_NAME, d->config.name, name_len); memcpy(p + ANN_NAME, d->config.name, name_len);
return TRANSPORT_FRAME_HEADER_SIZE + payload_len;
}
static void send_announcement(struct Discovery *d) {
uint8_t buf[TRANSPORT_FRAME_HEADER_SIZE + ANN_FIXED_SIZE + DISCOVERY_MAX_NAME_LEN];
size_t total = build_announcement(d, buf);
sendto(d->sock, buf, total, 0, sendto(d->sock, buf, total, 0,
(struct sockaddr *)&d->mcast_addr, sizeof(d->mcast_addr)); (struct sockaddr *)&d->mcast_addr, sizeof(d->mcast_addr));
} }
static void send_announcement_unicast(struct Discovery *d, uint32_t addr) {
uint8_t buf[TRANSPORT_FRAME_HEADER_SIZE + ANN_FIXED_SIZE + DISCOVERY_MAX_NAME_LEN];
size_t total = build_announcement(d, buf);
struct sockaddr_in dest = {0};
dest.sin_family = AF_INET;
dest.sin_port = htons(DISCOVERY_PORT);
dest.sin_addr.s_addr = addr;
sendto(d->sock, buf, total, 0, (struct sockaddr *)&dest, sizeof(dest));
}
/* -- timeout check --------------------------------------------------------- */ /* -- timeout check --------------------------------------------------------- */
static void check_timeouts(struct Discovery *d) { static void check_timeouts(struct Discovery *d) {
@@ -237,11 +248,10 @@ static void *receive_thread_fn(void *arg) {
pthread_mutex_unlock(&d->peers_mutex); pthread_mutex_unlock(&d->peers_mutex);
if (is_new || reannounce) { if (is_new || reannounce) {
/* new peer, or peer restarted (site_id changed) — announce ourselves /* new peer, or peer restarted (site_id changed) — reply directly
* immediately so it learns about us without waiting up to interval_ms */ * to that host so it learns about us without waiting up to interval_ms.
pthread_mutex_lock(&d->announce_mutex); * Use unicast rather than multicast to avoid disturbing other nodes. */
pthread_cond_signal(&d->announce_cond); send_announcement_unicast(d, addr);
pthread_mutex_unlock(&d->announce_mutex);
} }
if (is_new && d->config.on_peer_found) { if (is_new && d->config.on_peer_found) {