]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
resolved: implement mDNS probing and announcement
authorDmitry Rozhkov <dmitry.rozhkov@linux.intel.com>
Fri, 2 Dec 2016 13:01:56 +0000 (15:01 +0200)
committerDmitry Rozhkov <dmitry.rozhkov@linux.intel.com>
Thu, 19 Jan 2017 09:51:21 +0000 (11:51 +0200)
Signed-off-by: Dmitry Rozhkov <dmitry.rozhkov@linux.intel.com>
src/resolve/resolved-dns-scope.c
src/resolve/resolved-dns-scope.h
src/resolve/resolved-dns-transaction.c
src/resolve/resolved-dns-transaction.h
src/resolve/resolved-dns-zone.c
src/resolve/resolved-mdns.h

index 2c23a0df7718a1aca88dcab014fcf28137da2f40..6616bc4e0e54b9c98b9bae5c7921a7427917a657 100644 (file)
@@ -124,6 +124,8 @@ DnsScope* dns_scope_free(DnsScope *s) {
         ordered_hashmap_free(s->conflict_queue);
         sd_event_source_unref(s->conflict_event_source);
 
+        sd_event_source_unref(s->announce_event_source);
+
         dns_cache_flush(&s->cache);
         dns_zone_flush(&s->zone);
 
@@ -791,8 +793,15 @@ DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsResourceKey *key,
         /* Try to find an ongoing transaction that is a equal to the
          * specified question */
         t = hashmap_get(scope->transactions_by_key, key);
-        if (!t)
-                return NULL;
+        if (!t) {
+                DnsResourceKey *key_any;
+
+                key_any = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_ANY, dns_resource_key_name(key));
+                t = hashmap_get(scope->transactions_by_key, key_any);
+                key_any = dns_resource_key_unref(key_any);
+                if (!t)
+                        return NULL;
+        }
 
         /* Refuse reusing transactions that completed based on cached
          * data instead of a real packet, if that's requested. */
@@ -936,17 +945,19 @@ void dns_scope_check_conflicts(DnsScope *scope, DnsPacket *p) {
         assert(scope);
         assert(p);
 
-        if (p->protocol != DNS_PROTOCOL_LLMNR)
+        if (!IN_SET(p->protocol, DNS_PROTOCOL_LLMNR, DNS_PROTOCOL_MDNS))
                 return;
 
         if (DNS_PACKET_RRCOUNT(p) <= 0)
                 return;
 
-        if (DNS_PACKET_LLMNR_C(p) != 0)
-                return;
+        if (p->protocol == DNS_PROTOCOL_LLMNR) {
+                if (DNS_PACKET_LLMNR_C(p) != 0)
+                        return;
 
-        if (DNS_PACKET_LLMNR_T(p) != 0)
-                return;
+                if (DNS_PACKET_LLMNR_T(p) != 0)
+                        return;
+        }
 
         if (manager_our_packet(scope->manager, p))
                 return;
@@ -1049,3 +1060,76 @@ int dns_scope_ifindex(DnsScope *s) {
 
         return 0;
 }
+
+static int on_announcement_timeout(sd_event_source *s, usec_t usec, void *userdata) {
+        DnsScope *scope = userdata;
+
+        assert(s);
+
+        scope->announce_event_source = sd_event_source_unref(scope->announce_event_source);
+
+        dns_scope_announce(scope);
+        return 0;
+}
+
+void dns_scope_announce(DnsScope *scope) {
+        _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
+        _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
+        LinkAddress *a;
+        int r;
+
+        if (!scope)
+                return;
+
+        if (scope->protocol != DNS_PROTOCOL_MDNS)
+                return;
+
+        answer = dns_answer_new(4);
+        LIST_FOREACH(addresses, a, scope->link->addresses) {
+                r = dns_answer_add(answer, a->mdns_address_rr, 0);
+                if (r < 0) {
+                        log_debug_errno(r, "Failed to add address RR to answer: %m");
+                        return;
+                }
+                r = dns_answer_add(answer, a->mdns_ptr_rr, 0);
+                if (r < 0) {
+                        log_debug_errno(r, "Failed to add PTR RR to answer: %m");
+                        return;
+                }
+        }
+
+        if (dns_answer_isempty(answer))
+                return;
+
+        r = dns_scope_make_reply_packet(scope, 0, DNS_RCODE_SUCCESS, NULL, answer, NULL, false, &p);
+        if (r < 0) {
+                log_debug_errno(r, "Failed to build reply packet: %m");
+                return;
+        }
+        r = dns_scope_emit_udp(scope, -1, p);
+        if (r < 0) {
+                log_debug_errno(r, "Failed to send reply packet: %m");
+                return;
+        }
+
+        /* In section 8.3 of RFC6762: "The Multicast DNS responder MUST send at least two unsolicited
+         * responses, one second apart." */
+        if (!scope->announced) {
+                usec_t ts;
+
+                scope->announced = true;
+
+                assert_se(sd_event_now(scope->manager->event, clock_boottime_or_monotonic(), &ts) >= 0);
+                ts += MDNS_ANNOUNCE_DELAY;
+
+                r = sd_event_add_time(
+                                scope->manager->event,
+                                &scope->announce_event_source,
+                                clock_boottime_or_monotonic(),
+                                ts,
+                                MDNS_JITTER_RANGE_USEC,
+                                on_announcement_timeout, scope);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to schedule second announcement: %m");
+        }
+}
index d501835e459af3cae648f720aff857daf5f4a7dc..65b61c56aa12a4f7ffbfe0c89009ea0ba146deec 100644 (file)
@@ -56,6 +56,9 @@ struct DnsScope {
         OrderedHashmap *conflict_queue;
         sd_event_source *conflict_event_source;
 
+        bool announced:1;
+        sd_event_source *announce_event_source;
+
         RateLimit ratelimit;
 
         usec_t resend_timeout;
@@ -113,3 +116,5 @@ bool dns_scope_name_needs_search_domain(DnsScope *s, const char *name);
 bool dns_scope_network_good(DnsScope *s);
 
 int dns_scope_ifindex(DnsScope *s);
+
+void dns_scope_announce(DnsScope *scope);
index 03d25dd98485b836243a4b60688daa318153d04f..bd36e5e1a1ad1a229459c9801d96ff13f3d17d1b 100644 (file)
@@ -363,6 +363,9 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) {
         SET_FOREACH_MOVE(z, t->notify_zone_items_done, t->notify_zone_items)
                 dns_zone_item_notify(z);
         SWAP_TWO(t->notify_zone_items, t->notify_zone_items_done);
+        if (t->probing) {
+                dns_scope_announce(t->scope);
+        }
 
         SET_FOREACH_MOVE(d, t->notify_transactions_done, t->notify_transactions)
                 dns_transaction_notify(d, t);
@@ -1209,7 +1212,10 @@ static usec_t transaction_get_resend_timeout(DnsTransaction *t) {
 
         case DNS_PROTOCOL_MDNS:
                 assert(t->n_attempts > 0);
-                return (1 << (t->n_attempts - 1)) * USEC_PER_SEC;
+                if (t->probing)
+                        return MDNS_PROBING_INTERVAL_USEC;
+                else
+                        return (1 << (t->n_attempts - 1)) * USEC_PER_SEC;
 
         case DNS_PROTOCOL_LLMNR:
                 return t->scope->resend_timeout;
index 5a1df704220262cc2c61afe532311d7c80148d9d..c4fb51958db03a57472d56c8326ecbdfcf93c9e4 100644 (file)
@@ -78,6 +78,8 @@ struct DnsTransaction {
 
         bool clamp_ttl:1;
 
+        bool probing:1;
+
         DnsPacket *sent, *received;
 
         DnsAnswer *answer;
@@ -172,10 +174,20 @@ DnsTransactionSource dns_transaction_source_from_string(const char *s) _pure_;
 #define MDNS_JITTER_MIN_USEC   (20 * USEC_PER_MSEC)
 #define MDNS_JITTER_RANGE_USEC (100 * USEC_PER_MSEC)
 
+/* mDNS probing interval, see RFC 6762 Section 8.1 */
+#define MDNS_PROBING_INTERVAL_USEC (250 * USEC_PER_MSEC)
+
 /* Maximum attempts to send DNS requests, across all DNS servers */
 #define DNS_TRANSACTION_ATTEMPTS_MAX 16
 
 /* Maximum attempts to send LLMNR requests, see RFC 4795 Section 2.7 */
 #define LLMNR_TRANSACTION_ATTEMPTS_MAX 3
 
-#define TRANSACTION_ATTEMPTS_MAX(p) ((p) == DNS_PROTOCOL_LLMNR ? LLMNR_TRANSACTION_ATTEMPTS_MAX : DNS_TRANSACTION_ATTEMPTS_MAX)
+/* Maximum attempts to send MDNS requests, see RFC 6762 Section 8.1 */
+#define MDNS_TRANSACTION_ATTEMPTS_MAX 3
+
+#define TRANSACTION_ATTEMPTS_MAX(p) (((p) == DNS_PROTOCOL_LLMNR) ? \
+                                         LLMNR_TRANSACTION_ATTEMPTS_MAX : \
+                                         (((p) == DNS_PROTOCOL_MDNS) ? \
+                                             MDNS_TRANSACTION_ATTEMPTS_MAX : \
+                                             DNS_TRANSACTION_ATTEMPTS_MAX))
index 746a979f47d1d6c0f125735a04b8a5c1e3621ff0..ad024b54f5d4d474a30e92db49352758e998afe9 100644 (file)
@@ -196,6 +196,7 @@ static int dns_zone_item_probe_start(DnsZoneItem *i)  {
                 goto gc;
 
         i->probe_transaction = t;
+        t->probing = true;
 
         if (t->state == DNS_TRANSACTION_NULL) {
 
index 5d274648f4aa5393406b552c2341454427a57110..06bd3296bee20aad241cc8f47cd7b2325a2d121f 100644 (file)
@@ -22,6 +22,7 @@
 #include "resolved-manager.h"
 
 #define MDNS_PORT 5353
+#define MDNS_ANNOUNCE_DELAY (1 * USEC_PER_SEC)
 
 int manager_mdns_ipv4_fd(Manager *m);
 int manager_mdns_ipv6_fd(Manager *m);