]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-mdns.c
Merge pull request #30513 from rpigott/resolved-ede
[thirdparty/systemd.git] / src / resolve / resolved-mdns.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
bc7702b0
DM
2
3#include <resolv.h>
4#include <netinet/in.h>
5#include <arpa/inet.h>
6
8d67e72c 7#include "alloc-util.h"
bc7702b0
DM
8#include "fd-util.h"
9#include "resolved-manager.h"
10#include "resolved-mdns.h"
760877e9 11#include "sort-util.h"
bc7702b0 12
82d39576 13#define CLEAR_CACHE_FLUSH(x) (~MDNS_RR_CACHE_FLUSH_OR_QU & (x))
8d67e72c 14
bc7702b0
DM
15void manager_mdns_stop(Manager *m) {
16 assert(m);
17
97935302 18 m->mdns_ipv4_event_source = sd_event_source_disable_unref(m->mdns_ipv4_event_source);
bc7702b0
DM
19 m->mdns_ipv4_fd = safe_close(m->mdns_ipv4_fd);
20
97935302 21 m->mdns_ipv6_event_source = sd_event_source_disable_unref(m->mdns_ipv6_event_source);
bc7702b0
DM
22 m->mdns_ipv6_fd = safe_close(m->mdns_ipv6_fd);
23}
24
25int manager_mdns_start(Manager *m) {
26 int r;
27
28 assert(m);
29
af49ca27 30 if (m->mdns_support == RESOLVE_SUPPORT_NO)
bc7702b0
DM
31 return 0;
32
33 r = manager_mdns_ipv4_fd(m);
34 if (r == -EADDRINUSE)
35 goto eaddrinuse;
36 if (r < 0)
37 return r;
38
a53082f0 39 if (socket_ipv6_is_enabled()) {
bc7702b0
DM
40 r = manager_mdns_ipv6_fd(m);
41 if (r == -EADDRINUSE)
42 goto eaddrinuse;
43 if (r < 0)
44 return r;
45 }
46
47 return 0;
48
49eaddrinuse:
71a047d2 50 log_warning("Another mDNS responder prohibits binding the socket to the same port. Turning off mDNS support.");
af49ca27 51 m->mdns_support = RESOLVE_SUPPORT_NO;
bc7702b0
DM
52 manager_mdns_stop(m);
53
54 return 0;
55}
56
93bab288
YW
57static int mdns_rr_compare(DnsResourceRecord * const *a, DnsResourceRecord * const *b) {
58 DnsResourceRecord *x = *(DnsResourceRecord **) a, *y = *(DnsResourceRecord **) b;
8d67e72c
DR
59 size_t m;
60 int r;
61
62 assert(x);
8d67e72c 63 assert(y);
8d67e72c 64
93bab288
YW
65 r = CMP(CLEAR_CACHE_FLUSH(x->key->class), CLEAR_CACHE_FLUSH(y->key->class));
66 if (r != 0)
67 return r;
8d67e72c 68
93bab288
YW
69 r = CMP(x->key->type, y->key->type);
70 if (r != 0)
71 return r;
8d67e72c 72
93bab288 73 r = dns_resource_record_to_wire_format(x, false);
8d67e72c
DR
74 if (r < 0) {
75 log_warning_errno(r, "Can't wire-format RR: %m");
76 return 0;
77 }
78
93bab288 79 r = dns_resource_record_to_wire_format(y, false);
8d67e72c
DR
80 if (r < 0) {
81 log_warning_errno(r, "Can't wire-format RR: %m");
82 return 0;
83 }
84
93bab288 85 m = MIN(DNS_RESOURCE_RECORD_RDATA_SIZE(x), DNS_RESOURCE_RECORD_RDATA_SIZE(y));
8d67e72c 86
93bab288 87 r = memcmp(DNS_RESOURCE_RECORD_RDATA(x), DNS_RESOURCE_RECORD_RDATA(y), m);
8d67e72c
DR
88 if (r != 0)
89 return r;
90
93bab288 91 return CMP(DNS_RESOURCE_RECORD_RDATA_SIZE(x), DNS_RESOURCE_RECORD_RDATA_SIZE(y));
8d67e72c
DR
92}
93
94static int proposed_rrs_cmp(DnsResourceRecord **x, unsigned x_size, DnsResourceRecord **y, unsigned y_size) {
95 unsigned m;
96 int r;
97
98 m = MIN(x_size, y_size);
99 for (unsigned i = 0; i < m; i++) {
100 r = mdns_rr_compare(&x[i], &y[i]);
101 if (r != 0)
102 return r;
103 }
104
6dd91b36 105 return CMP(x_size, y_size);
8d67e72c
DR
106}
107
108static int mdns_packet_extract_matching_rrs(DnsPacket *p, DnsResourceKey *key, DnsResourceRecord ***ret_rrs) {
109 _cleanup_free_ DnsResourceRecord **list = NULL;
71aee23d
YW
110 size_t i, n = 0, size = 0;
111 DnsResourceRecord *rr;
8d67e72c
DR
112 int r;
113
114 assert(p);
115 assert(key);
116 assert(ret_rrs);
117 assert_return(DNS_PACKET_NSCOUNT(p) > 0, -EINVAL);
118
71aee23d
YW
119 i = 0;
120 DNS_ANSWER_FOREACH(rr, p->answer) {
121 if (i >= DNS_PACKET_ANCOUNT(p) && i < DNS_PACKET_ANCOUNT(p) + DNS_PACKET_NSCOUNT(p)) {
122 r = dns_resource_key_match_rr(key, rr, NULL);
123 if (r < 0)
124 return r;
125 if (r > 0)
126 size++;
127 }
128 i++;
8d67e72c
DR
129 }
130
71aee23d
YW
131 if (size == 0) {
132 *ret_rrs = NULL;
8d67e72c 133 return 0;
71aee23d 134 }
8d67e72c
DR
135
136 list = new(DnsResourceRecord *, size);
137 if (!list)
138 return -ENOMEM;
139
71aee23d
YW
140 i = 0;
141 DNS_ANSWER_FOREACH(rr, p->answer) {
142 if (i >= DNS_PACKET_ANCOUNT(p) && i < DNS_PACKET_ANCOUNT(p) + DNS_PACKET_NSCOUNT(p)) {
143 r = dns_resource_key_match_rr(key, rr, NULL);
144 if (r < 0)
145 return r;
146 if (r > 0)
147 list[n++] = rr;
148 }
149 i++;
8d67e72c 150 }
71aee23d 151
8d67e72c 152 assert(n == size);
93bab288 153 typesafe_qsort(list, size, mdns_rr_compare);
8d67e72c 154
1cc6c93a 155 *ret_rrs = TAKE_PTR(list);
8d67e72c
DR
156
157 return size;
158}
159
160static int mdns_do_tiebreak(DnsResourceKey *key, DnsAnswer *answer, DnsPacket *p) {
161 _cleanup_free_ DnsResourceRecord **our = NULL, **remote = NULL;
162 DnsResourceRecord *rr;
da6053d0 163 size_t i = 0, size;
8d67e72c
DR
164 int r;
165
166 size = dns_answer_size(answer);
167 our = new(DnsResourceRecord *, size);
168 if (!our)
169 return -ENOMEM;
170
171 DNS_ANSWER_FOREACH(rr, answer)
172 our[i++] = rr;
93bab288
YW
173
174 typesafe_qsort(our, size, mdns_rr_compare);
8d67e72c
DR
175
176 r = mdns_packet_extract_matching_rrs(p, key, &remote);
177 if (r < 0)
178 return r;
179
8d67e72c
DR
180 if (proposed_rrs_cmp(remote, r, our, size) > 0)
181 return 1;
182
183 return 0;
184}
185
84b0ff0e
SB
186static bool mdns_should_reply_using_unicast(DnsPacket *p) {
187 DnsQuestionItem *item;
188
189 /* Work out if we should respond using multicast or unicast. */
190
191 /* The query was a legacy "one-shot mDNS query", RFC 6762, sections 5.1 and 6.7 */
192 if (p->sender_port != MDNS_PORT)
193 return true;
194
195 /* The query was a "direct unicast query", RFC 6762, section 5.5 */
196 switch (p->family) {
197 case AF_INET:
198 if (!in4_addr_equal(&p->destination.in, &MDNS_MULTICAST_IPV4_ADDRESS))
199 return true;
200 break;
201 case AF_INET6:
202 if (!in6_addr_equal(&p->destination.in6, &MDNS_MULTICAST_IPV6_ADDRESS))
203 return true;
204 break;
205 }
206
207 /* All the questions in the query had a QU bit set, RFC 6762, section 5.4 */
d3887b2b 208 DNS_QUESTION_FOREACH_ITEM(item, p->question)
84b0ff0e
SB
209 if (!FLAGS_SET(item->flags, DNS_QUESTION_WANTS_UNICAST_REPLY))
210 return false;
d3887b2b 211
84b0ff0e
SB
212 return true;
213}
214
215static bool sender_on_local_subnet(DnsScope *s, DnsPacket *p) {
84b0ff0e
SB
216 int r;
217
218 /* Check whether the sender is on a local subnet. */
219
220 if (!s->link)
221 return false;
222
223 LIST_FOREACH(addresses, a, s->link->addresses) {
224 if (a->family != p->family)
225 continue;
226 if (a->prefixlen == UCHAR_MAX) /* don't know subnet mask */
227 continue;
228
229 r = in_addr_prefix_covers(a->family, &a->in_addr, a->prefixlen, &p->sender);
230 if (r < 0)
231 log_debug_errno(r, "Failed to determine whether link address covers sender address: %m");
232 if (r > 0)
233 return true;
234 }
235
236 return false;
237}
238
239
3b991089 240static int mdns_scope_process_query(DnsScope *s, DnsPacket *p) {
9c5e7b73 241 _cleanup_(dns_answer_unrefp) DnsAnswer *full_answer = NULL;
3b991089
DR
242 _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
243 DnsResourceKey *key = NULL;
8d67e72c 244 DnsResourceRecord *rr;
3b991089 245 bool tentative = false;
84b0ff0e
SB
246 bool legacy_query = p->sender_port != MDNS_PORT;
247 bool unicast_reply;
3b991089
DR
248 int r;
249
250 assert(s);
251 assert(p);
252
253 r = dns_packet_extract(p);
1a63fc54
LP
254 if (r < 0)
255 return log_debug_errno(r, "Failed to extract resource records from incoming packet: %m");
3b991089 256
dd51c433 257 /* TODO: Support Known-Answers only packets gracefully. */
055acd4d 258 if (dns_question_size(p->question) <= 0)
dd51c433 259 return 0;
3b991089 260
84b0ff0e
SB
261 unicast_reply = mdns_should_reply_using_unicast(p);
262 if (unicast_reply && !sender_on_local_subnet(s, p)) {
263 /* RFC 6762, section 5.5 recommends silently ignoring unicast queries
264 * from senders outside the local network, so that we don't reveal our
265 * internal network structure to outsiders. */
266 log_debug("Sender wants a unicast reply, but is not on a local subnet. Ignoring.");
267 return 0;
268 }
269
9c5e7b73
DR
270 DNS_QUESTION_FOREACH(key, p->question) {
271 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
84b0ff0e 272 DnsAnswerItem *item;
9c5e7b73
DR
273
274 r = dns_zone_lookup(&s->zone, key, 0, &answer, &soa, &tentative);
275 if (r < 0)
105a1a36 276 return log_debug_errno(r, "Failed to look up key: %m");
9c5e7b73 277
8d67e72c
DR
278 if (tentative && DNS_PACKET_NSCOUNT(p) > 0) {
279 /*
280 * A race condition detected with the probe packet from
281 * a remote host.
282 * Do simultaneous probe tiebreaking as described in
283 * RFC 6762, Section 8.2. In case we lost don't reply
284 * the question and withdraw conflicting RRs.
285 */
286 r = mdns_do_tiebreak(key, answer, p);
287 if (r < 0)
288 return log_debug_errno(r, "Failed to do tiebreaking");
289
290 if (r > 0) { /* we lost */
291 DNS_ANSWER_FOREACH(rr, answer) {
292 DnsZoneItem *i;
293
294 i = dns_zone_get(&s->zone, rr);
295 if (i)
296 dns_zone_item_conflict(i);
297 }
298
299 continue;
300 }
301 }
302
84b0ff0e
SB
303 if (dns_answer_isempty(answer))
304 continue;
305
306 /* Copy answer items from full_answer to answer, tweaking them if needed. */
307 if (full_answer) {
308 r = dns_answer_reserve(&full_answer, dns_answer_size(answer));
309 if (r < 0)
310 return log_debug_errno(r, "Failed to reserve space in answer");
311 } else {
312 full_answer = dns_answer_new(dns_answer_size(answer));
313 if (!full_answer)
314 return log_oom();
315 }
316
317 DNS_ANSWER_FOREACH_ITEM(item, answer) {
8ec951e8 318 DnsAnswerFlags flags = item->flags | DNS_ANSWER_REFUSE_TTL_NO_MATCH;
84b0ff0e
SB
319 /* The cache-flush bit must not be set in legacy unicast responses.
320 * See section 6.7 of RFC 6762. */
321 if (legacy_query)
322 flags &= ~DNS_ANSWER_CACHE_FLUSH;
323 r = dns_answer_add(full_answer, item->rr, item->ifindex, flags, item->rrsig);
324 if (r < 0)
325 return log_debug_errno(r, "Failed to extend answer: %m");
326 }
9c5e7b73
DR
327 }
328
329 if (dns_answer_isempty(full_answer))
3b991089
DR
330 return 0;
331
84b0ff0e
SB
332 r = dns_scope_make_reply_packet(s, DNS_PACKET_ID(p), DNS_RCODE_SUCCESS,
333 legacy_query ? p->question : NULL, full_answer,
334 NULL, false, &reply);
9886b6b1
LP
335 if (r < 0)
336 return log_debug_errno(r, "Failed to build reply packet: %m");
3b991089 337
7994ac1d 338 if (!ratelimit_below(&s->ratelimit))
3b991089
DR
339 return 0;
340
84b0ff0e
SB
341 if (unicast_reply) {
342 reply->destination = p->sender;
343 reply->destination_port = p->sender_port;
344 }
d79677ab 345 r = dns_scope_emit_udp(s, -1, AF_UNSPEC, reply);
9886b6b1
LP
346 if (r < 0)
347 return log_debug_errno(r, "Failed to send reply packet: %m");
3b991089
DR
348
349 return 0;
350}
351
bc7702b0 352static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
124602ae
DM
353 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
354 Manager *m = userdata;
355 DnsScope *scope;
356 int r;
357
358 r = manager_recv(m, fd, DNS_PROTOCOL_MDNS, &p);
359 if (r <= 0)
360 return r;
361
94378145 362 if (manager_packet_from_local_address(m, p))
cfb17f45
DR
363 return 0;
364
124602ae
DM
365 scope = manager_find_scope(m, p);
366 if (!scope) {
f1b1a5c4 367 log_debug("Got mDNS UDP packet on unknown scope. Ignoring.");
124602ae
DM
368 return 0;
369 }
370
371 if (dns_packet_validate_reply(p) > 0) {
40fa4728 372 DnsResourceRecord *rr;
124602ae
DM
373
374 log_debug("Got mDNS reply packet");
375
376 /*
377 * mDNS is different from regular DNS and LLMNR with regard to handling responses.
378 * While on other protocols, we can ignore every answer that doesn't match a question
379 * we broadcast earlier, RFC6762, section 18.1 recommends looking at and caching all
380 * incoming information, regardless of the DNS packet ID.
381 *
382 * Hence, extract the packet here, and try to find a transaction for answer the we got
383 * and complete it. Also store the new information in scope's cache.
384 */
385 r = dns_packet_extract(p);
386 if (r < 0) {
387 log_debug("mDNS packet extraction failed.");
388 return 0;
389 }
390
391 dns_scope_check_conflicts(scope, p);
392
40fa4728 393 DNS_ANSWER_FOREACH(rr, p->answer) {
8640566a
LP
394 const char *name;
395
396 name = dns_resource_key_name(rr->key);
124602ae 397
76755015
SB
398 /* If the received reply packet contains ANY record that is not .local
399 * or .in-addr.arpa or .ip6.arpa, we assume someone's playing tricks on
400 * us and discard the packet completely. */
40fa4728 401 if (!(dns_name_endswith(name, "in-addr.arpa") > 0 ||
76755015 402 dns_name_endswith(name, "ip6.arpa") > 0 ||
40fa4728
DM
403 dns_name_endswith(name, "local") > 0))
404 return 0;
124602ae 405
3755027c
DR
406 if (rr->ttl == 0) {
407 log_debug("Got a goodbye packet");
408 /* See the section 10.1 of RFC6762 */
409 rr->ttl = 1;
410 }
8640566a 411 }
3755027c 412
d50a58e7
YW
413 for (bool match = true; match;) {
414 match = false;
415 LIST_FOREACH(transactions_by_scope, t, scope->transactions) {
416 if (t->state != DNS_TRANSACTION_PENDING)
417 continue;
418
419 r = dns_answer_match_key(p->answer, dns_transaction_key(t), NULL);
420 if (r <= 0) {
421 if (r < 0)
422 log_debug_errno(r, "Failed to match resource key, ignoring: %m");
423 continue;
424 }
425
426 /* This packet matches the transaction, let's pass it on as reply */
43fc4baa 427 dns_transaction_process_reply(t, p, false);
d50a58e7
YW
428
429 /* The dns_transaction_process_reply() -> dns_transaction_complete() ->
430 * dns_query_candidate_stop() may free multiple transactions. Hence, restart
431 * the loop. */
432 match = true;
433 break;
434 }
124602ae
DM
435 }
436
a78049fc
YW
437 dns_cache_put(
438 &scope->cache,
439 scope->manager->enable_cache,
440 DNS_PROTOCOL_MDNS,
441 NULL,
442 DNS_PACKET_RCODE(p),
443 p->answer,
444 NULL,
445 false,
446 _DNSSEC_RESULT_INVALID,
447 UINT32_MAX,
448 p->family,
5ed91481
KV
449 &p->sender,
450 scope->manager->stale_retention_usec);
124602ae
DM
451
452 } else if (dns_packet_validate_query(p) > 0) {
453 log_debug("Got mDNS query packet for id %u", DNS_PACKET_ID(p));
454
3b991089
DR
455 r = mdns_scope_process_query(scope, p);
456 if (r < 0) {
1a63fc54 457 log_debug_errno(r, "mDNS query processing failed: %m");
3b991089
DR
458 return 0;
459 }
124602ae
DM
460 } else
461 log_debug("Invalid mDNS UDP packet.");
462
bc7702b0
DM
463 return 0;
464}
465
466int manager_mdns_ipv4_fd(Manager *m) {
467 union sockaddr_union sa = {
468 .in.sin_family = AF_INET,
469 .in.sin_port = htobe16(MDNS_PORT),
470 };
5bb1d7fb 471 _cleanup_close_ int s = -EBADF;
bc7702b0
DM
472 int r;
473
474 assert(m);
475
476 if (m->mdns_ipv4_fd >= 0)
477 return m->mdns_ipv4_fd;
478
73c76767
YW
479 s = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
480 if (s < 0)
71a047d2 481 return log_error_errno(errno, "mDNS-IPv4: Failed to create socket: %m");
bc7702b0 482
73c76767
YW
483 r = setsockopt_int(s, IPPROTO_IP, IP_TTL, 255);
484 if (r < 0)
485 return log_error_errno(r, "mDNS-IPv4: Failed to set IP_TTL: %m");
bc7702b0 486
73c76767
YW
487 r = setsockopt_int(s, IPPROTO_IP, IP_MULTICAST_TTL, 255);
488 if (r < 0)
489 return log_error_errno(r, "mDNS-IPv4: Failed to set IP_MULTICAST_TTL: %m");
bc7702b0 490
73c76767
YW
491 r = setsockopt_int(s, IPPROTO_IP, IP_MULTICAST_LOOP, true);
492 if (r < 0)
493 return log_error_errno(r, "mDNS-IPv4: Failed to set IP_MULTICAST_LOOP: %m");
bc7702b0 494
73c76767
YW
495 r = setsockopt_int(s, IPPROTO_IP, IP_PKTINFO, true);
496 if (r < 0)
497 return log_error_errno(r, "mDNS-IPv4: Failed to set IP_PKTINFO: %m");
bc7702b0 498
73c76767
YW
499 r = setsockopt_int(s, IPPROTO_IP, IP_RECVTTL, true);
500 if (r < 0)
501 return log_error_errno(r, "mDNS-IPv4: Failed to set IP_RECVTTL: %m");
bc7702b0
DM
502
503 /* Disable Don't-Fragment bit in the IP header */
73c76767
YW
504 r = setsockopt_int(s, IPPROTO_IP, IP_MTU_DISCOVER, IP_PMTUDISC_DONT);
505 if (r < 0)
506 return log_error_errno(r, "mDNS-IPv4: Failed to set IP_MTU_DISCOVER: %m");
bc7702b0 507
71a047d2
YW
508 /* See the section 15.1 of RFC6762 */
509 /* first try to bind without SO_REUSEADDR to detect another mDNS responder */
73c76767 510 r = bind(s, &sa.sa, sizeof(sa.in));
bc7702b0 511 if (r < 0) {
73c76767
YW
512 if (errno != EADDRINUSE)
513 return log_error_errno(errno, "mDNS-IPv4: Failed to bind socket: %m");
71a047d2
YW
514
515 log_warning("mDNS-IPv4: There appears to be another mDNS responder running, or previously systemd-resolved crashed with some outstanding transfers.");
516
517 /* try again with SO_REUSEADDR */
73c76767
YW
518 r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
519 if (r < 0)
520 return log_error_errno(r, "mDNS-IPv4: Failed to set SO_REUSEADDR: %m");
71a047d2 521
73c76767
YW
522 r = bind(s, &sa.sa, sizeof(sa.in));
523 if (r < 0)
524 return log_error_errno(errno, "mDNS-IPv4: Failed to bind socket: %m");
71a047d2
YW
525 } else {
526 /* enable SO_REUSEADDR for the case that the user really wants multiple mDNS responders */
73c76767
YW
527 r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
528 if (r < 0)
529 return log_error_errno(r, "mDNS-IPv4: Failed to set SO_REUSEADDR: %m");
bc7702b0
DM
530 }
531
73c76767 532 r = sd_event_add_io(m->event, &m->mdns_ipv4_event_source, s, EPOLLIN, on_mdns_packet, m);
bc7702b0 533 if (r < 0)
73c76767 534 return log_error_errno(r, "mDNS-IPv4: Failed to create event source: %m");
bc7702b0 535
a38f3cee
EV
536 (void) sd_event_source_set_description(m->mdns_ipv4_event_source, "mdns-ipv4");
537
73c76767 538 return m->mdns_ipv4_fd = TAKE_FD(s);
bc7702b0
DM
539}
540
541int manager_mdns_ipv6_fd(Manager *m) {
542 union sockaddr_union sa = {
543 .in6.sin6_family = AF_INET6,
544 .in6.sin6_port = htobe16(MDNS_PORT),
545 };
5bb1d7fb 546 _cleanup_close_ int s = -EBADF;
bc7702b0
DM
547 int r;
548
549 assert(m);
550
551 if (m->mdns_ipv6_fd >= 0)
552 return m->mdns_ipv6_fd;
553
73c76767
YW
554 s = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
555 if (s < 0)
71a047d2 556 return log_error_errno(errno, "mDNS-IPv6: Failed to create socket: %m");
bc7702b0 557
73c76767
YW
558 r = setsockopt_int(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS, 255);
559 if (r < 0)
560 return log_error_errno(r, "mDNS-IPv6: Failed to set IPV6_UNICAST_HOPS: %m");
bc7702b0 561
7fcded40 562 /* RFC 6762, section 11 recommends setting the TTL of UDP packets to 255. */
73c76767
YW
563 r = setsockopt_int(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 255);
564 if (r < 0)
565 return log_error_errno(r, "mDNS-IPv6: Failed to set IPV6_MULTICAST_HOPS: %m");
bc7702b0 566
73c76767
YW
567 r = setsockopt_int(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, true);
568 if (r < 0)
569 return log_error_errno(r, "mDNS-IPv6: Failed to set IPV6_MULTICAST_LOOP: %m");
bc7702b0 570
73c76767
YW
571 r = setsockopt_int(s, IPPROTO_IPV6, IPV6_V6ONLY, true);
572 if (r < 0)
573 return log_error_errno(r, "mDNS-IPv6: Failed to set IPV6_V6ONLY: %m");
bc7702b0 574
73c76767
YW
575 r = setsockopt_int(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, true);
576 if (r < 0)
577 return log_error_errno(r, "mDNS-IPv6: Failed to set IPV6_RECVPKTINFO: %m");
bc7702b0 578
73c76767
YW
579 r = setsockopt_int(s, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, true);
580 if (r < 0)
581 return log_error_errno(r, "mDNS-IPv6: Failed to set IPV6_RECVHOPLIMIT: %m");
bc7702b0 582
71a047d2
YW
583 /* See the section 15.1 of RFC6762 */
584 /* first try to bind without SO_REUSEADDR to detect another mDNS responder */
73c76767 585 r = bind(s, &sa.sa, sizeof(sa.in6));
bc7702b0 586 if (r < 0) {
73c76767
YW
587 if (errno != EADDRINUSE)
588 return log_error_errno(errno, "mDNS-IPv6: Failed to bind socket: %m");
71a047d2
YW
589
590 log_warning("mDNS-IPv6: There appears to be another mDNS responder running, or previously systemd-resolved crashed with some outstanding transfers.");
591
592 /* try again with SO_REUSEADDR */
73c76767
YW
593 r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
594 if (r < 0)
595 return log_error_errno(r, "mDNS-IPv6: Failed to set SO_REUSEADDR: %m");
71a047d2 596
73c76767
YW
597 r = bind(s, &sa.sa, sizeof(sa.in6));
598 if (r < 0)
599 return log_error_errno(errno, "mDNS-IPv6: Failed to bind socket: %m");
71a047d2
YW
600 } else {
601 /* enable SO_REUSEADDR for the case that the user really wants multiple mDNS responders */
73c76767
YW
602 r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
603 if (r < 0)
604 return log_error_errno(r, "mDNS-IPv6: Failed to set SO_REUSEADDR: %m");
bc7702b0
DM
605 }
606
73c76767 607 r = sd_event_add_io(m->event, &m->mdns_ipv6_event_source, s, EPOLLIN, on_mdns_packet, m);
ee8d9305 608 if (r < 0)
73c76767 609 return log_error_errno(r, "mDNS-IPv6: Failed to create event source: %m");
bc7702b0 610
a38f3cee
EV
611 (void) sd_event_source_set_description(m->mdns_ipv6_event_source, "mdns-ipv6");
612
73c76767 613 return m->mdns_ipv6_fd = TAKE_FD(s);
bc7702b0 614}