]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-link.c
resolved: add code to join/leave mDNS multicast groups
[thirdparty/systemd.git] / src / resolve / resolved-link.c
CommitLineData
74b2466e
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2014 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <net/if.h>
23
24#include "sd-network.h"
07630cea 25
b5efdb8a 26#include "alloc-util.h"
ec2c5e43 27#include "missing.h"
6bedfcbb 28#include "parse-util.h"
74b2466e 29#include "resolved-link.h"
07630cea
LP
30#include "string-util.h"
31#include "strv.h"
74b2466e
LP
32
33int link_new(Manager *m, Link **ret, int ifindex) {
34 _cleanup_(link_freep) Link *l = NULL;
35 int r;
36
37 assert(m);
38 assert(ifindex > 0);
39
d5099efc 40 r = hashmap_ensure_allocated(&m->links, NULL);
74b2466e
LP
41 if (r < 0)
42 return r;
43
44 l = new0(Link, 1);
45 if (!l)
46 return -ENOMEM;
47
48 l->ifindex = ifindex;
19b50b5b 49 l->llmnr_support = SUPPORT_YES;
74b2466e
LP
50
51 r = hashmap_put(m->links, INT_TO_PTR(ifindex), l);
52 if (r < 0)
53 return r;
54
55 l->manager = m;
56
57 if (ret)
58 *ret = l;
59 l = NULL;
60
61 return 0;
62}
63
64Link *link_free(Link *l) {
74b2466e
LP
65 if (!l)
66 return NULL;
67
4b95f179 68 dns_server_unlink_marked(l->dns_servers);
a51c1048 69 dns_search_domain_unlink_all(l->search_domains);
0eac4623 70
74b2466e
LP
71 while (l->addresses)
72 link_address_free(l->addresses);
73
74 if (l->manager)
75 hashmap_remove(l->manager->links, INT_TO_PTR(l->ifindex));
76
77 dns_scope_free(l->unicast_scope);
1716f6dc
LP
78 dns_scope_free(l->llmnr_ipv4_scope);
79 dns_scope_free(l->llmnr_ipv6_scope);
74b2466e 80
74b2466e
LP
81 free(l);
82 return NULL;
1716f6dc
LP
83}
84
85static void link_allocate_scopes(Link *l) {
86 int r;
87
88 assert(l);
89
6073b6f2 90 if (l->dns_servers) {
1716f6dc
LP
91 if (!l->unicast_scope) {
92 r = dns_scope_new(l->manager, &l->unicast_scope, l, DNS_PROTOCOL_DNS, AF_UNSPEC);
93 if (r < 0)
da927ba9 94 log_warning_errno(r, "Failed to allocate DNS scope: %m");
1716f6dc
LP
95 }
96 } else
97 l->unicast_scope = dns_scope_free(l->unicast_scope);
98
90ab5042
LP
99 if (link_relevant(l, AF_INET) &&
100 l->llmnr_support != SUPPORT_NO &&
db97a66a 101 l->manager->llmnr_support != SUPPORT_NO) {
1716f6dc
LP
102 if (!l->llmnr_ipv4_scope) {
103 r = dns_scope_new(l->manager, &l->llmnr_ipv4_scope, l, DNS_PROTOCOL_LLMNR, AF_INET);
104 if (r < 0)
da927ba9 105 log_warning_errno(r, "Failed to allocate LLMNR IPv4 scope: %m");
1716f6dc
LP
106 }
107 } else
108 l->llmnr_ipv4_scope = dns_scope_free(l->llmnr_ipv4_scope);
109
90ab5042
LP
110 if (link_relevant(l, AF_INET6) &&
111 l->llmnr_support != SUPPORT_NO &&
112 l->manager->llmnr_support != SUPPORT_NO &&
db97a66a 113 socket_ipv6_is_supported()) {
1716f6dc
LP
114 if (!l->llmnr_ipv6_scope) {
115 r = dns_scope_new(l->manager, &l->llmnr_ipv6_scope, l, DNS_PROTOCOL_LLMNR, AF_INET6);
116 if (r < 0)
da927ba9 117 log_warning_errno(r, "Failed to allocate LLMNR IPv6 scope: %m");
1716f6dc
LP
118 }
119 } else
120 l->llmnr_ipv6_scope = dns_scope_free(l->llmnr_ipv6_scope);
121}
74b2466e 122
ec2c5e43 123void link_add_rrs(Link *l, bool force_remove) {
623a4c97
LP
124 LinkAddress *a;
125
126 LIST_FOREACH(addresses, a, l->addresses)
ec2c5e43 127 link_address_add_rrs(a, force_remove);
623a4c97
LP
128}
129
1c4baffc 130int link_update_rtnl(Link *l, sd_netlink_message *m) {
1716f6dc 131 const char *n = NULL;
74b2466e
LP
132 int r;
133
134 assert(l);
135 assert(m);
136
137 r = sd_rtnl_message_link_get_flags(m, &l->flags);
138 if (r < 0)
139 return r;
140
1c4baffc 141 sd_netlink_message_read_u32(m, IFLA_MTU, &l->mtu);
1716f6dc 142
1c4baffc 143 if (sd_netlink_message_read_string(m, IFLA_IFNAME, &n) >= 0) {
cc7844e7 144 strncpy(l->name, n, sizeof(l->name)-1);
1716f6dc
LP
145 char_array_0(l->name);
146 }
147
148 link_allocate_scopes(l);
ec2c5e43 149 link_add_rrs(l, false);
623a4c97 150
74b2466e
LP
151 return 0;
152}
153
6073b6f2 154static int link_update_dns_servers(Link *l) {
6f4dedb2
TG
155 _cleanup_strv_free_ char **nameservers = NULL;
156 char **nameserver;
6f4dedb2 157 int r;
74b2466e
LP
158
159 assert(l);
160
d6731e4c 161 r = sd_network_link_get_dns(l->ifindex, &nameservers);
6f4dedb2 162 if (r < 0)
74b2466e 163 goto clear;
74b2466e 164
4b95f179 165 dns_server_mark_all(l->dns_servers);
5cb36f41 166
6f4dedb2
TG
167 STRV_FOREACH(nameserver, nameservers) {
168 union in_addr_union a;
0eac4623 169 DnsServer *s;
6f4dedb2 170 int family;
74b2466e 171
6f4dedb2
TG
172 r = in_addr_from_string_auto(*nameserver, &family, &a);
173 if (r < 0)
174 goto clear;
74b2466e 175
4b95f179 176 s = dns_server_find(l->dns_servers, family, &a);
74b2466e 177 if (s)
0b58db65 178 dns_server_move_back_and_unmark(s);
74b2466e 179 else {
4e945a6f 180 r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, family, &a);
74b2466e
LP
181 if (r < 0)
182 goto clear;
183 }
184 }
185
4b95f179 186 dns_server_unlink_marked(l->dns_servers);
74b2466e
LP
187 return 0;
188
189clear:
4b95f179 190 dns_server_unlink_all(l->dns_servers);
74b2466e
LP
191 return r;
192}
193
19b50b5b
LP
194static int link_update_llmnr_support(Link *l) {
195 _cleanup_free_ char *b = NULL;
196 int r;
197
198 assert(l);
199
d6731e4c 200 r = sd_network_link_get_llmnr(l->ifindex, &b);
19b50b5b
LP
201 if (r < 0)
202 goto clear;
203
204 r = parse_boolean(b);
205 if (r < 0) {
206 if (streq(b, "resolve"))
207 l->llmnr_support = SUPPORT_RESOLVE;
208 else
209 goto clear;
210
211 } else if (r > 0)
212 l->llmnr_support = SUPPORT_YES;
213 else
214 l->llmnr_support = SUPPORT_NO;
215
216 return 0;
217
218clear:
219 l->llmnr_support = SUPPORT_YES;
220 return r;
221}
222
a51c1048
LP
223static int link_update_search_domains(Link *l) {
224 _cleanup_strv_free_ char **domains = NULL;
225 char **i;
bda2c408
TG
226 int r;
227
a51c1048 228 assert(l);
bda2c408 229
a51c1048 230 r = sd_network_link_get_domains(l->ifindex, &domains);
bda2c408 231 if (r < 0)
a51c1048
LP
232 goto clear;
233
234 dns_search_domain_mark_all(l->search_domains);
235
236 STRV_FOREACH(i, domains) {
237 DnsSearchDomain *d;
bda2c408 238
a51c1048
LP
239 r = dns_search_domain_find(l->search_domains, *i, &d);
240 if (r < 0)
241 goto clear;
242
243 if (r > 0)
244 dns_search_domain_move_back_and_unmark(d);
245 else {
246 r = dns_search_domain_new(l->manager, NULL, DNS_SEARCH_DOMAIN_LINK, l, *i);
247 if (r < 0)
248 goto clear;
249 }
250 }
251
252 dns_search_domain_unlink_marked(l->search_domains);
bda2c408 253 return 0;
a51c1048
LP
254
255clear:
256 dns_search_domain_unlink_all(l->search_domains);
257 return r;
bda2c408
TG
258}
259
74b2466e 260int link_update_monitor(Link *l) {
a51c1048
LP
261 int r;
262
74b2466e
LP
263 assert(l);
264
6073b6f2 265 link_update_dns_servers(l);
19b50b5b 266 link_update_llmnr_support(l);
1716f6dc 267 link_allocate_scopes(l);
a51c1048
LP
268
269 r = link_update_search_domains(l);
270 if (r < 0)
271 log_warning_errno(r, "Failed to read search domains for interface %s, ignoring: %m", l->name);
272
ec2c5e43 273 link_add_rrs(l, false);
74b2466e
LP
274
275 return 0;
276}
277
0dd25fb9 278bool link_relevant(Link *l, int family) {
1716f6dc 279 _cleanup_free_ char *state = NULL;
74b2466e
LP
280 LinkAddress *a;
281
282 assert(l);
283
ec2c5e43
LP
284 /* A link is relevant if it isn't a loopback or pointopoint
285 * device, has a link beat, can do multicast and has at least
286 * one relevant IP address */
287
288 if (l->flags & (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_DORMANT))
289 return false;
74b2466e 290
ec2c5e43 291 if ((l->flags & (IFF_UP|IFF_LOWER_UP|IFF_MULTICAST)) != (IFF_UP|IFF_LOWER_UP|IFF_MULTICAST))
74b2466e
LP
292 return false;
293
d6731e4c 294 sd_network_link_get_operational_state(l->ifindex, &state);
1716f6dc 295 if (state && !STR_IN_SET(state, "unknown", "degraded", "routable"))
74b2466e
LP
296 return false;
297
298 LIST_FOREACH(addresses, a, l->addresses)
1716f6dc 299 if (a->family == family && link_address_relevant(a))
74b2466e
LP
300 return true;
301
302 return false;
303}
304
623a4c97 305LinkAddress *link_find_address(Link *l, int family, const union in_addr_union *in_addr) {
74b2466e
LP
306 LinkAddress *a;
307
308 assert(l);
309
1716f6dc
LP
310 LIST_FOREACH(addresses, a, l->addresses)
311 if (a->family == family && in_addr_equal(family, &a->in_addr, in_addr))
74b2466e 312 return a;
74b2466e
LP
313
314 return NULL;
315}
316
2c27fbca 317DnsServer* link_set_dns_server(Link *l, DnsServer *s) {
4e945a6f
LP
318 assert(l);
319
320 if (l->current_dns_server == s)
321 return s;
322
323 if (s) {
324 _cleanup_free_ char *ip = NULL;
325
326 in_addr_to_string(s->family, &s->address, &ip);
327 log_info("Switching to DNS server %s for interface %s.", strna(ip), l->name);
2c27fbca 328 }
4e945a6f 329
0eac4623
LP
330 dns_server_unref(l->current_dns_server);
331 l->current_dns_server = dns_server_ref(s);
2c27fbca
LP
332
333 if (l->unicast_scope)
334 dns_cache_flush(&l->unicast_scope->cache);
335
4e945a6f
LP
336 return s;
337}
338
74b2466e
LP
339DnsServer *link_get_dns_server(Link *l) {
340 assert(l);
341
342 if (!l->current_dns_server)
4e945a6f 343 link_set_dns_server(l, l->dns_servers);
74b2466e
LP
344
345 return l->current_dns_server;
346}
347
348void link_next_dns_server(Link *l) {
349 assert(l);
350
74b2466e
LP
351 if (!l->current_dns_server)
352 return;
353
0eac4623
LP
354 /* Change to the next one, but make sure to follow the linked
355 * list only if this server is actually still linked. */
356 if (l->current_dns_server->linked && l->current_dns_server->servers_next) {
4e945a6f 357 link_set_dns_server(l, l->current_dns_server->servers_next);
74b2466e
LP
358 return;
359 }
360
4e945a6f 361 link_set_dns_server(l, l->dns_servers);
74b2466e
LP
362}
363
623a4c97 364int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr_union *in_addr) {
74b2466e
LP
365 LinkAddress *a;
366
367 assert(l);
368 assert(in_addr);
369
370 a = new0(LinkAddress, 1);
371 if (!a)
372 return -ENOMEM;
373
374 a->family = family;
375 a->in_addr = *in_addr;
376
377 a->link = l;
378 LIST_PREPEND(addresses, l->addresses, a);
379
380 if (ret)
381 *ret = a;
382
383 return 0;
384}
385
386LinkAddress *link_address_free(LinkAddress *a) {
387 if (!a)
388 return NULL;
389
623a4c97 390 if (a->link) {
74b2466e
LP
391 LIST_REMOVE(addresses, a->link->addresses, a);
392
623a4c97 393 if (a->llmnr_address_rr) {
623a4c97
LP
394 if (a->family == AF_INET && a->link->llmnr_ipv4_scope)
395 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr);
396 else if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope)
397 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr);
623a4c97
LP
398 }
399
400 if (a->llmnr_ptr_rr) {
401 if (a->family == AF_INET && a->link->llmnr_ipv4_scope)
402 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_ptr_rr);
403 else if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope)
404 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr);
623a4c97
LP
405 }
406 }
407
ec2c5e43
LP
408 dns_resource_record_unref(a->llmnr_address_rr);
409 dns_resource_record_unref(a->llmnr_ptr_rr);
410
74b2466e
LP
411 free(a);
412 return NULL;
413}
414
ec2c5e43 415void link_address_add_rrs(LinkAddress *a, bool force_remove) {
623a4c97
LP
416 int r;
417
418 assert(a);
419
ec2c5e43 420 if (a->family == AF_INET) {
623a4c97 421
4e945a6f
LP
422 if (!force_remove &&
423 link_address_relevant(a) &&
424 a->link->llmnr_ipv4_scope &&
19b50b5b 425 a->link->llmnr_support == SUPPORT_YES &&
4e945a6f
LP
426 a->link->manager->llmnr_support == SUPPORT_YES) {
427
78c6a153
LP
428 if (!a->link->manager->llmnr_host_ipv4_key) {
429 a->link->manager->llmnr_host_ipv4_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, a->link->manager->llmnr_hostname);
430 if (!a->link->manager->llmnr_host_ipv4_key) {
ec2c5e43
LP
431 r = -ENOMEM;
432 goto fail;
433 }
623a4c97 434 }
623a4c97 435
623a4c97 436 if (!a->llmnr_address_rr) {
78c6a153 437 a->llmnr_address_rr = dns_resource_record_new(a->link->manager->llmnr_host_ipv4_key);
ec2c5e43
LP
438 if (!a->llmnr_address_rr) {
439 r = -ENOMEM;
440 goto fail;
441 }
442
443 a->llmnr_address_rr->a.in_addr = a->in_addr.in;
444 a->llmnr_address_rr->ttl = LLMNR_DEFAULT_TTL;
623a4c97
LP
445 }
446
ec2c5e43 447 if (!a->llmnr_ptr_rr) {
78c6a153 448 r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->llmnr_hostname);
ec2c5e43
LP
449 if (r < 0)
450 goto fail;
623a4c97 451
ec2c5e43
LP
452 a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL;
453 }
623a4c97 454
ec2c5e43 455 r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_address_rr, true);
623a4c97 456 if (r < 0)
da927ba9 457 log_warning_errno(r, "Failed to add A record to LLMNR zone: %m");
623a4c97 458
ec2c5e43 459 r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_ptr_rr, false);
623a4c97 460 if (r < 0)
da927ba9 461 log_warning_errno(r, "Failed to add IPv6 PTR record to LLMNR zone: %m");
623a4c97 462 } else {
ec2c5e43
LP
463 if (a->llmnr_address_rr) {
464 if (a->link->llmnr_ipv4_scope)
465 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr);
466 a->llmnr_address_rr = dns_resource_record_unref(a->llmnr_address_rr);
467 }
468
469 if (a->llmnr_ptr_rr) {
470 if (a->link->llmnr_ipv4_scope)
471 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_ptr_rr);
472 a->llmnr_ptr_rr = dns_resource_record_unref(a->llmnr_ptr_rr);
473 }
623a4c97
LP
474 }
475 }
476
ec2c5e43 477 if (a->family == AF_INET6) {
623a4c97 478
4e945a6f
LP
479 if (!force_remove &&
480 link_address_relevant(a) &&
481 a->link->llmnr_ipv6_scope &&
19b50b5b 482 a->link->llmnr_support == SUPPORT_YES &&
4e945a6f
LP
483 a->link->manager->llmnr_support == SUPPORT_YES) {
484
78c6a153
LP
485 if (!a->link->manager->llmnr_host_ipv6_key) {
486 a->link->manager->llmnr_host_ipv6_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, a->link->manager->llmnr_hostname);
487 if (!a->link->manager->llmnr_host_ipv6_key) {
ec2c5e43
LP
488 r = -ENOMEM;
489 goto fail;
490 }
623a4c97 491 }
623a4c97 492
623a4c97 493 if (!a->llmnr_address_rr) {
78c6a153 494 a->llmnr_address_rr = dns_resource_record_new(a->link->manager->llmnr_host_ipv6_key);
ec2c5e43
LP
495 if (!a->llmnr_address_rr) {
496 r = -ENOMEM;
497 goto fail;
498 }
499
500 a->llmnr_address_rr->aaaa.in6_addr = a->in_addr.in6;
501 a->llmnr_address_rr->ttl = LLMNR_DEFAULT_TTL;
623a4c97
LP
502 }
503
ec2c5e43 504 if (!a->llmnr_ptr_rr) {
78c6a153 505 r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->llmnr_hostname);
ec2c5e43
LP
506 if (r < 0)
507 goto fail;
623a4c97 508
ec2c5e43
LP
509 a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL;
510 }
623a4c97 511
ec2c5e43 512 r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_address_rr, true);
623a4c97 513 if (r < 0)
da927ba9 514 log_warning_errno(r, "Failed to add AAAA record to LLMNR zone: %m");
623a4c97 515
ec2c5e43 516 r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_ptr_rr, false);
623a4c97 517 if (r < 0)
da927ba9 518 log_warning_errno(r, "Failed to add IPv6 PTR record to LLMNR zone: %m");
623a4c97 519 } else {
ec2c5e43
LP
520 if (a->llmnr_address_rr) {
521 if (a->link->llmnr_ipv6_scope)
522 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr);
523 a->llmnr_address_rr = dns_resource_record_unref(a->llmnr_address_rr);
524 }
525
526 if (a->llmnr_ptr_rr) {
527 if (a->link->llmnr_ipv6_scope)
528 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr);
529 a->llmnr_ptr_rr = dns_resource_record_unref(a->llmnr_ptr_rr);
530 }
623a4c97
LP
531 }
532 }
533
534 return;
535
536fail:
da927ba9 537 log_debug_errno(r, "Failed to update address RRs: %m");
623a4c97
LP
538}
539
1c4baffc 540int link_address_update_rtnl(LinkAddress *a, sd_netlink_message *m) {
74b2466e
LP
541 int r;
542 assert(a);
543 assert(m);
544
545 r = sd_rtnl_message_addr_get_flags(m, &a->flags);
546 if (r < 0)
547 return r;
548
1716f6dc 549 sd_rtnl_message_addr_get_scope(m, &a->scope);
74b2466e 550
1716f6dc 551 link_allocate_scopes(a->link);
ec2c5e43 552 link_add_rrs(a->link, false);
623a4c97 553
74b2466e
LP
554 return 0;
555}
556
557bool link_address_relevant(LinkAddress *a) {
558 assert(a);
559
7b85d72f 560 if (a->flags & (IFA_F_DEPRECATED|IFA_F_TENTATIVE))
74b2466e
LP
561 return false;
562
563 if (IN_SET(a->scope, RT_SCOPE_HOST, RT_SCOPE_NOWHERE))
564 return false;
565
566 return true;
567}