]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-link.c
resolved: add a generic DnsSearchDomain concept
[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 dns_search_domain_unlink_all(l->search_domains);
70
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);
78 dns_scope_free(l->llmnr_ipv4_scope);
79 dns_scope_free(l->llmnr_ipv6_scope);
80
81 free(l);
82 return NULL;
83 }
84
85 static void link_allocate_scopes(Link *l) {
86 int r;
87
88 assert(l);
89
90 if (l->dns_servers) {
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)
94 log_warning_errno(r, "Failed to allocate DNS scope: %m");
95 }
96 } else
97 l->unicast_scope = dns_scope_free(l->unicast_scope);
98
99 if (link_relevant(l, AF_INET) &&
100 l->llmnr_support != SUPPORT_NO &&
101 l->manager->llmnr_support != SUPPORT_NO) {
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)
105 log_warning_errno(r, "Failed to allocate LLMNR IPv4 scope: %m");
106 }
107 } else
108 l->llmnr_ipv4_scope = dns_scope_free(l->llmnr_ipv4_scope);
109
110 if (link_relevant(l, AF_INET6) &&
111 l->llmnr_support != SUPPORT_NO &&
112 l->manager->llmnr_support != SUPPORT_NO &&
113 socket_ipv6_is_supported()) {
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)
117 log_warning_errno(r, "Failed to allocate LLMNR IPv6 scope: %m");
118 }
119 } else
120 l->llmnr_ipv6_scope = dns_scope_free(l->llmnr_ipv6_scope);
121 }
122
123 void link_add_rrs(Link *l, bool force_remove) {
124 LinkAddress *a;
125
126 LIST_FOREACH(addresses, a, l->addresses)
127 link_address_add_rrs(a, force_remove);
128 }
129
130 int link_update_rtnl(Link *l, sd_netlink_message *m) {
131 const char *n = NULL;
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
141 sd_netlink_message_read_u32(m, IFLA_MTU, &l->mtu);
142
143 if (sd_netlink_message_read_string(m, IFLA_IFNAME, &n) >= 0) {
144 strncpy(l->name, n, sizeof(l->name)-1);
145 char_array_0(l->name);
146 }
147
148 link_allocate_scopes(l);
149 link_add_rrs(l, false);
150
151 return 0;
152 }
153
154 static int link_update_dns_servers(Link *l) {
155 _cleanup_strv_free_ char **nameservers = NULL;
156 char **nameserver;
157 int r;
158
159 assert(l);
160
161 r = sd_network_link_get_dns(l->ifindex, &nameservers);
162 if (r < 0)
163 goto clear;
164
165 link_mark_dns_servers(l);
166
167 STRV_FOREACH(nameserver, nameservers) {
168 union in_addr_union a;
169 DnsServer *s;
170 int family;
171
172 r = in_addr_from_string_auto(*nameserver, &family, &a);
173 if (r < 0)
174 goto clear;
175
176 s = link_find_dns_server(l, family, &a);
177 if (s)
178 dns_server_move_back_and_unmark(s);
179 else {
180 r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, family, &a);
181 if (r < 0)
182 goto clear;
183 }
184 }
185
186 link_flush_marked_dns_servers(l);
187 return 0;
188
189 clear:
190 link_flush_dns_servers(l);
191 return r;
192 }
193
194 static int link_update_llmnr_support(Link *l) {
195 _cleanup_free_ char *b = NULL;
196 int r;
197
198 assert(l);
199
200 r = sd_network_link_get_llmnr(l->ifindex, &b);
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
218 clear:
219 l->llmnr_support = SUPPORT_YES;
220 return r;
221 }
222
223 static int link_update_search_domains(Link *l) {
224 _cleanup_strv_free_ char **domains = NULL;
225 char **i;
226 int r;
227
228 assert(l);
229
230 r = sd_network_link_get_domains(l->ifindex, &domains);
231 if (r < 0)
232 goto clear;
233
234 dns_search_domain_mark_all(l->search_domains);
235
236 STRV_FOREACH(i, domains) {
237 DnsSearchDomain *d;
238
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);
253 return 0;
254
255 clear:
256 dns_search_domain_unlink_all(l->search_domains);
257 return r;
258 }
259
260 int link_update_monitor(Link *l) {
261 int r;
262
263 assert(l);
264
265 link_update_dns_servers(l);
266 link_update_llmnr_support(l);
267 link_allocate_scopes(l);
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
273 link_add_rrs(l, false);
274
275 return 0;
276 }
277
278 bool link_relevant(Link *l, int family) {
279 _cleanup_free_ char *state = NULL;
280 LinkAddress *a;
281
282 assert(l);
283
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;
290
291 if ((l->flags & (IFF_UP|IFF_LOWER_UP|IFF_MULTICAST)) != (IFF_UP|IFF_LOWER_UP|IFF_MULTICAST))
292 return false;
293
294 sd_network_link_get_operational_state(l->ifindex, &state);
295 if (state && !STR_IN_SET(state, "unknown", "degraded", "routable"))
296 return false;
297
298 LIST_FOREACH(addresses, a, l->addresses)
299 if (a->family == family && link_address_relevant(a))
300 return true;
301
302 return false;
303 }
304
305 LinkAddress *link_find_address(Link *l, int family, const union in_addr_union *in_addr) {
306 LinkAddress *a;
307
308 assert(l);
309
310 LIST_FOREACH(addresses, a, l->addresses)
311 if (a->family == family && in_addr_equal(family, &a->in_addr, in_addr))
312 return a;
313
314 return NULL;
315 }
316
317 void link_flush_dns_servers(Link *l) {
318 assert(l);
319
320 while (l->dns_servers)
321 dns_server_unlink(l->dns_servers);
322 }
323
324 void link_flush_marked_dns_servers(Link *l) {
325 DnsServer *s, *next;
326
327 assert(l);
328
329 LIST_FOREACH_SAFE(servers, s, next, l->dns_servers) {
330 if (!s->marked)
331 continue;
332
333 dns_server_unlink(s);
334 }
335 }
336
337 void link_mark_dns_servers(Link *l) {
338 DnsServer *s;
339
340 assert(l);
341
342 LIST_FOREACH(servers, s, l->dns_servers)
343 s->marked = true;
344 }
345
346 DnsServer* link_find_dns_server(Link *l, int family, const union in_addr_union *in_addr) {
347 DnsServer *s;
348
349 assert(l);
350 assert(in_addr);
351
352 LIST_FOREACH(servers, s, l->dns_servers)
353 if (s->family == family && in_addr_equal(family, &s->address, in_addr))
354 return s;
355 return NULL;
356 }
357
358 DnsServer* link_set_dns_server(Link *l, DnsServer *s) {
359 assert(l);
360
361 if (l->current_dns_server == s)
362 return s;
363
364 if (s) {
365 _cleanup_free_ char *ip = NULL;
366
367 in_addr_to_string(s->family, &s->address, &ip);
368 log_info("Switching to DNS server %s for interface %s.", strna(ip), l->name);
369 }
370
371 dns_server_unref(l->current_dns_server);
372 l->current_dns_server = dns_server_ref(s);
373
374 if (l->unicast_scope)
375 dns_cache_flush(&l->unicast_scope->cache);
376
377 return s;
378 }
379
380 DnsServer *link_get_dns_server(Link *l) {
381 assert(l);
382
383 if (!l->current_dns_server)
384 link_set_dns_server(l, l->dns_servers);
385
386 return l->current_dns_server;
387 }
388
389 void link_next_dns_server(Link *l) {
390 assert(l);
391
392 if (!l->current_dns_server)
393 return;
394
395 /* Change to the next one, but make sure to follow the linked
396 * list only if this server is actually still linked. */
397 if (l->current_dns_server->linked && l->current_dns_server->servers_next) {
398 link_set_dns_server(l, l->current_dns_server->servers_next);
399 return;
400 }
401
402 link_set_dns_server(l, l->dns_servers);
403 }
404
405 int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr_union *in_addr) {
406 LinkAddress *a;
407
408 assert(l);
409 assert(in_addr);
410
411 a = new0(LinkAddress, 1);
412 if (!a)
413 return -ENOMEM;
414
415 a->family = family;
416 a->in_addr = *in_addr;
417
418 a->link = l;
419 LIST_PREPEND(addresses, l->addresses, a);
420
421 if (ret)
422 *ret = a;
423
424 return 0;
425 }
426
427 LinkAddress *link_address_free(LinkAddress *a) {
428 if (!a)
429 return NULL;
430
431 if (a->link) {
432 LIST_REMOVE(addresses, a->link->addresses, a);
433
434 if (a->llmnr_address_rr) {
435 if (a->family == AF_INET && a->link->llmnr_ipv4_scope)
436 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr);
437 else if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope)
438 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr);
439 }
440
441 if (a->llmnr_ptr_rr) {
442 if (a->family == AF_INET && a->link->llmnr_ipv4_scope)
443 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_ptr_rr);
444 else if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope)
445 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr);
446 }
447 }
448
449 dns_resource_record_unref(a->llmnr_address_rr);
450 dns_resource_record_unref(a->llmnr_ptr_rr);
451
452 free(a);
453 return NULL;
454 }
455
456 void link_address_add_rrs(LinkAddress *a, bool force_remove) {
457 int r;
458
459 assert(a);
460
461 if (a->family == AF_INET) {
462
463 if (!force_remove &&
464 link_address_relevant(a) &&
465 a->link->llmnr_ipv4_scope &&
466 a->link->llmnr_support == SUPPORT_YES &&
467 a->link->manager->llmnr_support == SUPPORT_YES) {
468
469 if (!a->link->manager->llmnr_host_ipv4_key) {
470 a->link->manager->llmnr_host_ipv4_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, a->link->manager->llmnr_hostname);
471 if (!a->link->manager->llmnr_host_ipv4_key) {
472 r = -ENOMEM;
473 goto fail;
474 }
475 }
476
477 if (!a->llmnr_address_rr) {
478 a->llmnr_address_rr = dns_resource_record_new(a->link->manager->llmnr_host_ipv4_key);
479 if (!a->llmnr_address_rr) {
480 r = -ENOMEM;
481 goto fail;
482 }
483
484 a->llmnr_address_rr->a.in_addr = a->in_addr.in;
485 a->llmnr_address_rr->ttl = LLMNR_DEFAULT_TTL;
486 }
487
488 if (!a->llmnr_ptr_rr) {
489 r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->llmnr_hostname);
490 if (r < 0)
491 goto fail;
492
493 a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL;
494 }
495
496 r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_address_rr, true);
497 if (r < 0)
498 log_warning_errno(r, "Failed to add A record to LLMNR zone: %m");
499
500 r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_ptr_rr, false);
501 if (r < 0)
502 log_warning_errno(r, "Failed to add IPv6 PTR record to LLMNR zone: %m");
503 } else {
504 if (a->llmnr_address_rr) {
505 if (a->link->llmnr_ipv4_scope)
506 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr);
507 a->llmnr_address_rr = dns_resource_record_unref(a->llmnr_address_rr);
508 }
509
510 if (a->llmnr_ptr_rr) {
511 if (a->link->llmnr_ipv4_scope)
512 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_ptr_rr);
513 a->llmnr_ptr_rr = dns_resource_record_unref(a->llmnr_ptr_rr);
514 }
515 }
516 }
517
518 if (a->family == AF_INET6) {
519
520 if (!force_remove &&
521 link_address_relevant(a) &&
522 a->link->llmnr_ipv6_scope &&
523 a->link->llmnr_support == SUPPORT_YES &&
524 a->link->manager->llmnr_support == SUPPORT_YES) {
525
526 if (!a->link->manager->llmnr_host_ipv6_key) {
527 a->link->manager->llmnr_host_ipv6_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, a->link->manager->llmnr_hostname);
528 if (!a->link->manager->llmnr_host_ipv6_key) {
529 r = -ENOMEM;
530 goto fail;
531 }
532 }
533
534 if (!a->llmnr_address_rr) {
535 a->llmnr_address_rr = dns_resource_record_new(a->link->manager->llmnr_host_ipv6_key);
536 if (!a->llmnr_address_rr) {
537 r = -ENOMEM;
538 goto fail;
539 }
540
541 a->llmnr_address_rr->aaaa.in6_addr = a->in_addr.in6;
542 a->llmnr_address_rr->ttl = LLMNR_DEFAULT_TTL;
543 }
544
545 if (!a->llmnr_ptr_rr) {
546 r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->llmnr_hostname);
547 if (r < 0)
548 goto fail;
549
550 a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL;
551 }
552
553 r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_address_rr, true);
554 if (r < 0)
555 log_warning_errno(r, "Failed to add AAAA record to LLMNR zone: %m");
556
557 r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_ptr_rr, false);
558 if (r < 0)
559 log_warning_errno(r, "Failed to add IPv6 PTR record to LLMNR zone: %m");
560 } else {
561 if (a->llmnr_address_rr) {
562 if (a->link->llmnr_ipv6_scope)
563 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr);
564 a->llmnr_address_rr = dns_resource_record_unref(a->llmnr_address_rr);
565 }
566
567 if (a->llmnr_ptr_rr) {
568 if (a->link->llmnr_ipv6_scope)
569 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr);
570 a->llmnr_ptr_rr = dns_resource_record_unref(a->llmnr_ptr_rr);
571 }
572 }
573 }
574
575 return;
576
577 fail:
578 log_debug_errno(r, "Failed to update address RRs: %m");
579 }
580
581 int link_address_update_rtnl(LinkAddress *a, sd_netlink_message *m) {
582 int r;
583 assert(a);
584 assert(m);
585
586 r = sd_rtnl_message_addr_get_flags(m, &a->flags);
587 if (r < 0)
588 return r;
589
590 sd_rtnl_message_addr_get_scope(m, &a->scope);
591
592 link_allocate_scopes(a->link);
593 link_add_rrs(a->link, false);
594
595 return 0;
596 }
597
598 bool link_address_relevant(LinkAddress *a) {
599 assert(a);
600
601 if (a->flags & (IFA_F_DEPRECATED|IFA_F_TENTATIVE))
602 return false;
603
604 if (IN_SET(a->scope, RT_SCOPE_HOST, RT_SCOPE_NOWHERE))
605 return false;
606
607 return true;
608 }