resolved: remove entry from cache when goodbye packet received
authorVishal Chillara Srinivas <vishal.chillarasrinivas@philips.com>
Mon, 20 Nov 2023 06:33:28 +0000 (12:03 +0530)
committerLennart Poettering <lennart@poettering.net>
Tue, 5 Mar 2024 18:00:03 +0000 (19:00 +0100)
RFC6762 10.1 says that queriers receiving a Multicast DNS response with a TTL
of zero SHOULD record a TTL of 1 and then delete the record one second later.

Added a timer event to trigger a callback to clean-up the cache one second after
a goodbye packet is received. The callback also checks for any cache entries
expiring within the next one second and schedules follow-up cleanup callbacks
accordingly.

src/resolve/resolved-dns-cache.c
src/resolve/resolved-dns-cache.h
src/resolve/resolved-dns-scope.c
src/resolve/resolved-dns-scope.h
src/resolve/resolved-mdns.c

index ec92406a7e5b22c2e4fb1ba165b88ed2b29763e3..e778bd48c84a7873a724cc5900a9875f10015755 100644 (file)
@@ -243,6 +243,22 @@ void dns_cache_prune(DnsCache *c) {
         }
 }
 
+bool dns_cache_expiry_in_one_second(DnsCache *c, usec_t t) {
+        DnsCacheItem *i;
+
+        assert(c);
+
+        /* Check if any items expire within the next second */
+        i = prioq_peek(c->by_expiry);
+        if (!i)
+                return false;
+
+        if (i->until <= usec_add(t, USEC_PER_SEC))
+                return true;
+
+        return false;
+}
+
 static int dns_cache_item_prioq_compare_func(const void *a, const void *b) {
         const DnsCacheItem *x = a, *y = b;
 
index d078ae9872c96831e6e1363cf2ab073841fd6833..6a45b95a600f2e304ecd687394d0fcaf42cbd42d 100644 (file)
@@ -58,3 +58,5 @@ bool dns_cache_is_empty(DnsCache *cache);
 unsigned dns_cache_size(DnsCache *cache);
 
 int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p, usec_t ts, unsigned max_rr);
+
+bool dns_cache_expiry_in_one_second(DnsCache *c, usec_t t);
index e390715807f625f422622b0062fe23fc90fbd3ce..f13233c8da2b77f7dfb52425b887a2beb5f02e05 100644 (file)
@@ -41,6 +41,7 @@ int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int
                 .protocol = protocol,
                 .family = family,
                 .resend_timeout = MULTICAST_RESEND_TIMEOUT_MIN_USEC,
+                .mdns_goodbye_event_source = NULL,
         };
 
         if (protocol == DNS_PROTOCOL_DNS) {
@@ -115,6 +116,8 @@ DnsScope* dns_scope_free(DnsScope *s) {
 
         sd_event_source_disable_unref(s->announce_event_source);
 
+        sd_event_source_disable_unref(s->mdns_goodbye_event_source);
+
         dns_cache_flush(&s->cache);
         dns_zone_flush(&s->zone);
 
index ca33fd007a6aeaf077d0364061242e6336440910..82218e70faa84ed3c603bec1bc640da1072aaf7d 100644 (file)
@@ -45,6 +45,8 @@ struct DnsScope {
 
         sd_event_source *announce_event_source;
 
+        sd_event_source *mdns_goodbye_event_source;
+
         RateLimit ratelimit;
 
         usec_t resend_timeout;
index 3e6e83fe62586d6f3a196242f5f2e953a8c58983..60ae0b8fbd055d71d349b6cb7fce605f5a807dac 100644 (file)
@@ -349,6 +349,33 @@ static int mdns_scope_process_query(DnsScope *s, DnsPacket *p) {
         return 0;
 }
 
+static int mdns_goodbye_callback(sd_event_source *s, uint64_t usec, void *userdata) {
+        DnsScope *scope = userdata;
+        int r;
+
+        assert(s);
+        assert(scope);
+
+        scope->mdns_goodbye_event_source = sd_event_source_disable_unref(scope->mdns_goodbye_event_source);
+
+        dns_cache_prune(&scope->cache);
+
+        if (dns_cache_expiry_in_one_second(&scope->cache, usec)) {
+                r = sd_event_add_time_relative(
+                        scope->manager->event,
+                        &scope->mdns_goodbye_event_source,
+                        CLOCK_BOOTTIME,
+                        USEC_PER_SEC,
+                        0,
+                        mdns_goodbye_callback,
+                        scope);
+                if (r < 0)
+                        return log_error_errno(r, "mDNS: Failed to re-schedule goodbye callback: %m");
+        }
+
+        return 0;
+}
+
 static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
         _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
         Manager *m = userdata;
@@ -407,6 +434,22 @@ static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *us
                                 log_debug("Got a goodbye packet");
                                 /* See the section 10.1 of RFC6762 */
                                 rr->ttl = 1;
+
+                                /* Look at the cache 1 second later and remove stale entries.
+                                 * This is particularly useful to keep service browsers updated on service removal,
+                                 * as there are no other reliable triggers to propogate that info. */
+                                if (!scope->mdns_goodbye_event_source) {
+                                        r = sd_event_add_time_relative(
+                                                        scope->manager->event,
+                                                        &scope->mdns_goodbye_event_source,
+                                                        CLOCK_BOOTTIME,
+                                                        USEC_PER_SEC,
+                                                        0,
+                                                        mdns_goodbye_callback,
+                                                        scope);
+                                        if (r < 0)
+                                                return r;
+                                }
                         }
                 }