]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-link.c
resolved: rework dns server lifecycle logic
[thirdparty/systemd.git] / src / resolve / resolved-link.c
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"
25
26 #include "alloc-util.h"
27 #include "missing.h"
28 #include "parse-util.h"
29 #include "resolved-link.h"
30 #include "string-util.h"
31 #include "strv.h"
32
33 int 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
40 r = hashmap_ensure_allocated(&m->links, NULL);
41 if (r < 0)
42 return r;
43
44 l = new0(Link, 1);
45 if (!l)
46 return -ENOMEM;
47
48 l->ifindex = ifindex;
49 l->llmnr_support = SUPPORT_YES;
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
64 Link *link_free(Link *l) {
65 if (!l)
66 return NULL;
67
68 link_flush_dns_servers(l);
69
70 while (l->addresses)
71 link_address_free(l->addresses);
72
73 if (l->manager)
74 hashmap_remove(l->manager->links, INT_TO_PTR(l->ifindex));
75
76 dns_scope_free(l->unicast_scope);
77 dns_scope_free(l->llmnr_ipv4_scope);
78 dns_scope_free(l->llmnr_ipv6_scope);
79
80 free(l);
81 return NULL;
82 }
83
84 static void link_allocate_scopes(Link *l) {
85 int r;
86
87 assert(l);
88
89 if (l->dns_servers) {
90 if (!l->unicast_scope) {
91 r = dns_scope_new(l->manager, &l->unicast_scope, l, DNS_PROTOCOL_DNS, AF_UNSPEC);
92 if (r < 0)
93 log_warning_errno(r, "Failed to allocate DNS scope: %m");
94 }
95 } else
96 l->unicast_scope = dns_scope_free(l->unicast_scope);
97
98 if (link_relevant(l, AF_INET) &&
99 l->llmnr_support != SUPPORT_NO &&
100 l->manager->llmnr_support != SUPPORT_NO) {
101 if (!l->llmnr_ipv4_scope) {
102 r = dns_scope_new(l->manager, &l->llmnr_ipv4_scope, l, DNS_PROTOCOL_LLMNR, AF_INET);
103 if (r < 0)
104 log_warning_errno(r, "Failed to allocate LLMNR IPv4 scope: %m");
105 }
106 } else
107 l->llmnr_ipv4_scope = dns_scope_free(l->llmnr_ipv4_scope);
108
109 if (link_relevant(l, AF_INET6) &&
110 l->llmnr_support != SUPPORT_NO &&
111 l->manager->llmnr_support != SUPPORT_NO &&
112 socket_ipv6_is_supported()) {
113 if (!l->llmnr_ipv6_scope) {
114 r = dns_scope_new(l->manager, &l->llmnr_ipv6_scope, l, DNS_PROTOCOL_LLMNR, AF_INET6);
115 if (r < 0)
116 log_warning_errno(r, "Failed to allocate LLMNR IPv6 scope: %m");
117 }
118 } else
119 l->llmnr_ipv6_scope = dns_scope_free(l->llmnr_ipv6_scope);
120 }
121
122 void link_add_rrs(Link *l, bool force_remove) {
123 LinkAddress *a;
124
125 LIST_FOREACH(addresses, a, l->addresses)
126 link_address_add_rrs(a, force_remove);
127 }
128
129 int link_update_rtnl(Link *l, sd_netlink_message *m) {
130 const char *n = NULL;
131 int r;
132
133 assert(l);
134 assert(m);
135
136 r = sd_rtnl_message_link_get_flags(m, &l->flags);
137 if (r < 0)
138 return r;
139
140 sd_netlink_message_read_u32(m, IFLA_MTU, &l->mtu);
141
142 if (sd_netlink_message_read_string(m, IFLA_IFNAME, &n) >= 0) {
143 strncpy(l->name, n, sizeof(l->name)-1);
144 char_array_0(l->name);
145 }
146
147 link_allocate_scopes(l);
148 link_add_rrs(l, false);
149
150 return 0;
151 }
152
153 static int link_update_dns_servers(Link *l) {
154 _cleanup_strv_free_ char **nameservers = NULL;
155 char **nameserver;
156 int r;
157
158 assert(l);
159
160 r = sd_network_link_get_dns(l->ifindex, &nameservers);
161 if (r < 0)
162 goto clear;
163
164 link_mark_dns_servers(l);
165
166 STRV_FOREACH(nameserver, nameservers) {
167 union in_addr_union a;
168 DnsServer *s;
169 int family;
170
171 r = in_addr_from_string_auto(*nameserver, &family, &a);
172 if (r < 0)
173 goto clear;
174
175 s = link_find_dns_server(l, family, &a);
176 if (s)
177 s->marked = false;
178 else {
179 r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, family, &a);
180 if (r < 0)
181 goto clear;
182 }
183 }
184
185 link_flush_marked_dns_servers(l);
186 return 0;
187
188 clear:
189 link_flush_dns_servers(l);
190 return r;
191 }
192
193 static int link_update_llmnr_support(Link *l) {
194 _cleanup_free_ char *b = NULL;
195 int r;
196
197 assert(l);
198
199 r = sd_network_link_get_llmnr(l->ifindex, &b);
200 if (r < 0)
201 goto clear;
202
203 r = parse_boolean(b);
204 if (r < 0) {
205 if (streq(b, "resolve"))
206 l->llmnr_support = SUPPORT_RESOLVE;
207 else
208 goto clear;
209
210 } else if (r > 0)
211 l->llmnr_support = SUPPORT_YES;
212 else
213 l->llmnr_support = SUPPORT_NO;
214
215 return 0;
216
217 clear:
218 l->llmnr_support = SUPPORT_YES;
219 return r;
220 }
221
222 static int link_update_domains(Link *l) {
223 int r;
224
225 if (!l->unicast_scope)
226 return 0;
227
228 l->unicast_scope->domains = strv_free(l->unicast_scope->domains);
229
230 r = sd_network_link_get_domains(l->ifindex,
231 &l->unicast_scope->domains);
232 if (r < 0)
233 return r;
234
235 return 0;
236 }
237
238 int link_update_monitor(Link *l) {
239 assert(l);
240
241 link_update_dns_servers(l);
242 link_update_llmnr_support(l);
243 link_allocate_scopes(l);
244 link_update_domains(l);
245 link_add_rrs(l, false);
246
247 return 0;
248 }
249
250 bool link_relevant(Link *l, int family) {
251 _cleanup_free_ char *state = NULL;
252 LinkAddress *a;
253
254 assert(l);
255
256 /* A link is relevant if it isn't a loopback or pointopoint
257 * device, has a link beat, can do multicast and has at least
258 * one relevant IP address */
259
260 if (l->flags & (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_DORMANT))
261 return false;
262
263 if ((l->flags & (IFF_UP|IFF_LOWER_UP|IFF_MULTICAST)) != (IFF_UP|IFF_LOWER_UP|IFF_MULTICAST))
264 return false;
265
266 sd_network_link_get_operational_state(l->ifindex, &state);
267 if (state && !STR_IN_SET(state, "unknown", "degraded", "routable"))
268 return false;
269
270 LIST_FOREACH(addresses, a, l->addresses)
271 if (a->family == family && link_address_relevant(a))
272 return true;
273
274 return false;
275 }
276
277 LinkAddress *link_find_address(Link *l, int family, const union in_addr_union *in_addr) {
278 LinkAddress *a;
279
280 assert(l);
281
282 LIST_FOREACH(addresses, a, l->addresses)
283 if (a->family == family && in_addr_equal(family, &a->in_addr, in_addr))
284 return a;
285
286 return NULL;
287 }
288
289 void link_flush_dns_servers(Link *l) {
290 assert(l);
291
292 while (l->dns_servers)
293 dns_server_unlink(l->dns_servers);
294 }
295
296 void link_flush_marked_dns_servers(Link *l) {
297 DnsServer *s, *next;
298
299 assert(l);
300
301 LIST_FOREACH_SAFE(servers, s, next, l->dns_servers) {
302 if (!s->marked)
303 continue;
304
305 dns_server_unlink(s);
306 }
307 }
308
309 void link_mark_dns_servers(Link *l) {
310 DnsServer *s;
311
312 assert(l);
313
314 LIST_FOREACH(servers, s, l->dns_servers)
315 s->marked = true;
316 }
317
318 DnsServer* link_find_dns_server(Link *l, int family, const union in_addr_union *in_addr) {
319 DnsServer *s;
320
321 assert(l);
322 assert(in_addr);
323
324 LIST_FOREACH(servers, s, l->dns_servers)
325 if (s->family == family && in_addr_equal(family, &s->address, in_addr))
326 return s;
327 return NULL;
328 }
329
330 DnsServer* link_set_dns_server(Link *l, DnsServer *s) {
331 assert(l);
332
333 if (l->current_dns_server == s)
334 return s;
335
336 if (s) {
337 _cleanup_free_ char *ip = NULL;
338
339 in_addr_to_string(s->family, &s->address, &ip);
340 log_info("Switching to DNS server %s for interface %s.", strna(ip), l->name);
341 }
342
343 dns_server_unref(l->current_dns_server);
344 l->current_dns_server = dns_server_ref(s);
345
346 if (l->unicast_scope)
347 dns_cache_flush(&l->unicast_scope->cache);
348
349 return s;
350 }
351
352 DnsServer *link_get_dns_server(Link *l) {
353 assert(l);
354
355 if (!l->current_dns_server)
356 link_set_dns_server(l, l->dns_servers);
357
358 return l->current_dns_server;
359 }
360
361 void link_next_dns_server(Link *l) {
362 assert(l);
363
364 if (!l->current_dns_server)
365 return;
366
367 /* Change to the next one, but make sure to follow the linked
368 * list only if this server is actually still linked. */
369 if (l->current_dns_server->linked && l->current_dns_server->servers_next) {
370 link_set_dns_server(l, l->current_dns_server->servers_next);
371 return;
372 }
373
374 link_set_dns_server(l, l->dns_servers);
375 }
376
377 int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr_union *in_addr) {
378 LinkAddress *a;
379
380 assert(l);
381 assert(in_addr);
382
383 a = new0(LinkAddress, 1);
384 if (!a)
385 return -ENOMEM;
386
387 a->family = family;
388 a->in_addr = *in_addr;
389
390 a->link = l;
391 LIST_PREPEND(addresses, l->addresses, a);
392
393 if (ret)
394 *ret = a;
395
396 return 0;
397 }
398
399 LinkAddress *link_address_free(LinkAddress *a) {
400 if (!a)
401 return NULL;
402
403 if (a->link) {
404 LIST_REMOVE(addresses, a->link->addresses, a);
405
406 if (a->llmnr_address_rr) {
407 if (a->family == AF_INET && a->link->llmnr_ipv4_scope)
408 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr);
409 else if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope)
410 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr);
411 }
412
413 if (a->llmnr_ptr_rr) {
414 if (a->family == AF_INET && a->link->llmnr_ipv4_scope)
415 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_ptr_rr);
416 else if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope)
417 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr);
418 }
419 }
420
421 dns_resource_record_unref(a->llmnr_address_rr);
422 dns_resource_record_unref(a->llmnr_ptr_rr);
423
424 free(a);
425 return NULL;
426 }
427
428 void link_address_add_rrs(LinkAddress *a, bool force_remove) {
429 int r;
430
431 assert(a);
432
433 if (a->family == AF_INET) {
434
435 if (!force_remove &&
436 link_address_relevant(a) &&
437 a->link->llmnr_ipv4_scope &&
438 a->link->llmnr_support == SUPPORT_YES &&
439 a->link->manager->llmnr_support == SUPPORT_YES) {
440
441 if (!a->link->manager->llmnr_host_ipv4_key) {
442 a->link->manager->llmnr_host_ipv4_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, a->link->manager->llmnr_hostname);
443 if (!a->link->manager->llmnr_host_ipv4_key) {
444 r = -ENOMEM;
445 goto fail;
446 }
447 }
448
449 if (!a->llmnr_address_rr) {
450 a->llmnr_address_rr = dns_resource_record_new(a->link->manager->llmnr_host_ipv4_key);
451 if (!a->llmnr_address_rr) {
452 r = -ENOMEM;
453 goto fail;
454 }
455
456 a->llmnr_address_rr->a.in_addr = a->in_addr.in;
457 a->llmnr_address_rr->ttl = LLMNR_DEFAULT_TTL;
458 }
459
460 if (!a->llmnr_ptr_rr) {
461 r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->llmnr_hostname);
462 if (r < 0)
463 goto fail;
464
465 a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL;
466 }
467
468 r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_address_rr, true);
469 if (r < 0)
470 log_warning_errno(r, "Failed to add A record to LLMNR zone: %m");
471
472 r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_ptr_rr, false);
473 if (r < 0)
474 log_warning_errno(r, "Failed to add IPv6 PTR record to LLMNR zone: %m");
475 } else {
476 if (a->llmnr_address_rr) {
477 if (a->link->llmnr_ipv4_scope)
478 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr);
479 a->llmnr_address_rr = dns_resource_record_unref(a->llmnr_address_rr);
480 }
481
482 if (a->llmnr_ptr_rr) {
483 if (a->link->llmnr_ipv4_scope)
484 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_ptr_rr);
485 a->llmnr_ptr_rr = dns_resource_record_unref(a->llmnr_ptr_rr);
486 }
487 }
488 }
489
490 if (a->family == AF_INET6) {
491
492 if (!force_remove &&
493 link_address_relevant(a) &&
494 a->link->llmnr_ipv6_scope &&
495 a->link->llmnr_support == SUPPORT_YES &&
496 a->link->manager->llmnr_support == SUPPORT_YES) {
497
498 if (!a->link->manager->llmnr_host_ipv6_key) {
499 a->link->manager->llmnr_host_ipv6_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, a->link->manager->llmnr_hostname);
500 if (!a->link->manager->llmnr_host_ipv6_key) {
501 r = -ENOMEM;
502 goto fail;
503 }
504 }
505
506 if (!a->llmnr_address_rr) {
507 a->llmnr_address_rr = dns_resource_record_new(a->link->manager->llmnr_host_ipv6_key);
508 if (!a->llmnr_address_rr) {
509 r = -ENOMEM;
510 goto fail;
511 }
512
513 a->llmnr_address_rr->aaaa.in6_addr = a->in_addr.in6;
514 a->llmnr_address_rr->ttl = LLMNR_DEFAULT_TTL;
515 }
516
517 if (!a->llmnr_ptr_rr) {
518 r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->llmnr_hostname);
519 if (r < 0)
520 goto fail;
521
522 a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL;
523 }
524
525 r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_address_rr, true);
526 if (r < 0)
527 log_warning_errno(r, "Failed to add AAAA record to LLMNR zone: %m");
528
529 r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_ptr_rr, false);
530 if (r < 0)
531 log_warning_errno(r, "Failed to add IPv6 PTR record to LLMNR zone: %m");
532 } else {
533 if (a->llmnr_address_rr) {
534 if (a->link->llmnr_ipv6_scope)
535 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr);
536 a->llmnr_address_rr = dns_resource_record_unref(a->llmnr_address_rr);
537 }
538
539 if (a->llmnr_ptr_rr) {
540 if (a->link->llmnr_ipv6_scope)
541 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr);
542 a->llmnr_ptr_rr = dns_resource_record_unref(a->llmnr_ptr_rr);
543 }
544 }
545 }
546
547 return;
548
549 fail:
550 log_debug_errno(r, "Failed to update address RRs: %m");
551 }
552
553 int link_address_update_rtnl(LinkAddress *a, sd_netlink_message *m) {
554 int r;
555 assert(a);
556 assert(m);
557
558 r = sd_rtnl_message_addr_get_flags(m, &a->flags);
559 if (r < 0)
560 return r;
561
562 sd_rtnl_message_addr_get_scope(m, &a->scope);
563
564 link_allocate_scopes(a->link);
565 link_add_rrs(a->link, false);
566
567 return 0;
568 }
569
570 bool link_address_relevant(LinkAddress *a) {
571 assert(a);
572
573 if (a->flags & (IFA_F_DEPRECATED|IFA_F_TENTATIVE))
574 return false;
575
576 if (IN_SET(a->scope, RT_SCOPE_HOST, RT_SCOPE_NOWHERE))
577 return false;
578
579 return true;
580 }