From d8d45ff4a51a89f7470bb404a471d4419ce6aacb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Guay-Lambert?= Date: Mon, 14 Sep 2026 18:03:20 -0400 Subject: [PATCH 1/2] cache: do not take our own address records for a probe conflict MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit At the end of probing, cache_host_is_known() treats any cached A or AAAA record for our host name as a conflict, and umdns stops announcing on that interface for good. On a network with an mDNS reflector, umdns receives its own probe and its own answers back, caches them, and so finds a conflict with itself: the host OpenWrt.local already exists. stopping announce service It then never announces its host or services on that interface. RFC 6762 section 9 only treats records with different data as a conflict. Ignore cached address records that carry one of our own interface addresses. Signed-off-by: Michaël Guay-Lambert --- cache.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/cache.c b/cache.c index 06c4eb6..f5805bc 100644 --- a/cache.c +++ b/cache.c @@ -264,6 +264,32 @@ cache_record_find(char *record, int type, int port, int rdlength, uint8_t *rdata return NULL; } +/* An address record carrying one of our own addresses is not a conflict: + * RFC 6762 section 9 only counts records whose data differs, and a network + * with an mDNS reflector sends our own announcements straight back. */ +static bool +cache_record_is_own_address(struct cache_record *r) +{ + struct interface *iface; + int i; + + vlist_for_each_element(&interfaces, iface, node) { + if (r->type == TYPE_A && !interface_ipv6(iface) && + r->rdlength == sizeof(struct in_addr)) { + for (i = 0; i < iface->addrs.n_addr; i++) + if (!memcmp(r->rdata, &iface->addrs.v4[i].addr, sizeof(struct in_addr))) + return true; + } else if (r->type == TYPE_AAAA && interface_ipv6(iface) && + r->rdlength == sizeof(struct in6_addr)) { + for (i = 0; i < iface->addrs.n_addr; i++) + if (!memcmp(r->rdata, &iface->addrs.v6[i].addr, sizeof(struct in6_addr))) + return true; + } + } + + return false; +} + int cache_host_is_known(char *record) { @@ -275,6 +301,8 @@ cache_host_is_known(char *record) l = !avl_is_last(&records, &l->avl) ? avl_next_element(l, avl) : NULL; if ((r->type != TYPE_A) && (r->type != TYPE_AAAA)) continue; + if (cache_record_is_own_address(r)) + continue; return 1; } From 82da8dbb02d3f6016a9a2ce53de5882dff143b83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Guay-Lambert?= Date: Mon, 14 Sep 2026 18:03:21 -0400 Subject: [PATCH 2/2] announce: announce service instances once probing is done MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit a2b4979 ("service: announce all services in single dns answer"), service_announce_services() only sends the service type PTR records, so an interface coming up announces the host records and the service types but not the service instances themselves (instance PTR, SRV and TXT). A service registered before its interface has an address, as happens at boot when a DHCP lease arrives after the service starts, is therefore never announced, and a browser that already dropped it only finds it again on its next query. RFC 6762 section 8.3 requires announcing all newly registered records. Send the service instances once probing succeeds, without adding them to the periodic refresh of STATE_ANNOUNCE. Signed-off-by: Michaël Guay-Lambert --- announce.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/announce.c b/announce.c index a562277..38ed1d7 100644 --- a/announce.c +++ b/announce.c @@ -62,6 +62,12 @@ announce_timer(struct uloop_timeout *timeout) return; } iface->announce_state++; + /* + * Announce the service instances along with the host records + * once probing is done (RFC 6762 section 8.3). STATE_ANNOUNCE + * only covers the host records and the service types. + */ + service_reply(iface, NULL, NULL, NULL, announce_ttl, 1); /* Fall through */ case STATE_ANNOUNCE: