From a5198e84d2238afc659b340e9b2e0345e8f0e3e8 Mon Sep 17 00:00:00 2001 From: mikael-lovqvists-claude-agent Date: Mon, 30 Mar 2026 04:31:56 +0000 Subject: [PATCH] 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 --- src/modules/discovery/discovery.c | 36 ++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/src/modules/discovery/discovery.c b/src/modules/discovery/discovery.c index afefd07..ea4f1d3 100644 --- a/src/modules/discovery/discovery.c +++ b/src/modules/discovery/discovery.c @@ -76,19 +76,14 @@ static int find_slot(struct Discovery *d) { /* -- send ------------------------------------------------------------------ */ -static void send_announcement(struct Discovery *d) { - size_t name_len = strlen(d->config.name); +static size_t build_announcement(struct Discovery *d, uint8_t *buf) { + size_t name_len = strlen(d->config.name); if (name_len > DISCOVERY_MAX_NAME_LEN) { name_len = DISCOVERY_MAX_NAME_LEN; } uint32_t payload_len = (uint32_t)(ANN_FIXED_SIZE + name_len); - size_t total = TRANSPORT_FRAME_HEADER_SIZE + payload_len; - 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_u16(buf, 0, 0x0010); put_u32(buf, 2, payload_len); - /* announcement payload */ uint8_t *p = buf + TRANSPORT_FRAME_HEADER_SIZE; put_u8 (p, ANN_PROTOCOL_VERSION, DISCOVERY_PROTOCOL_VERSION); 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); 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, (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 --------------------------------------------------------- */ static void check_timeouts(struct Discovery *d) { @@ -237,11 +248,10 @@ static void *receive_thread_fn(void *arg) { pthread_mutex_unlock(&d->peers_mutex); if (is_new || reannounce) { - /* new peer, or peer restarted (site_id changed) — announce ourselves - * immediately so it learns about us without waiting up to interval_ms */ - pthread_mutex_lock(&d->announce_mutex); - pthread_cond_signal(&d->announce_cond); - pthread_mutex_unlock(&d->announce_mutex); + /* new peer, or peer restarted (site_id changed) — reply directly + * to that host so it learns about us without waiting up to interval_ms. + * Use unicast rather than multicast to avoid disturbing other nodes. */ + send_announcement_unicast(d, addr); } if (is_new && d->config.on_peer_found) {