]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-link.c
resolved: add a generic DnsSearchDomain concept
[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
0eac4623 68 link_flush_dns_servers(l);
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
0eac4623 165 link_mark_dns_servers(l);
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
6f4dedb2 176 s = link_find_dns_server(l, 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
0eac4623 186 link_flush_marked_dns_servers(l);
74b2466e
LP
187 return 0;
188
189clear:
0eac4623 190 link_flush_dns_servers(l);
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
0eac4623
LP
317void link_flush_dns_servers(Link *l) {
318 assert(l);
319
320 while (l->dns_servers)
321 dns_server_unlink(l->dns_servers);
322}
323
324void 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
337void 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
623a4c97 346DnsServer* link_find_dns_server(Link *l, int family, const union in_addr_union *in_addr) {
6073b6f2 347 DnsServer *s;
74b2466e
LP
348
349 assert(l);
0eac4623 350 assert(in_addr);
74b2466e 351
6073b6f2 352 LIST_FOREACH(servers, s, l->dns_servers)
1716f6dc 353 if (s->family == family && in_addr_equal(family, &s->address, in_addr))
74b2466e 354 return s;
74b2466e
LP
355 return NULL;
356}
357
2c27fbca 358DnsServer* link_set_dns_server(Link *l, DnsServer *s) {
4e945a6f
LP
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);
2c27fbca 369 }
4e945a6f 370
0eac4623
LP
371 dns_server_unref(l->current_dns_server);
372 l->current_dns_server = dns_server_ref(s);
2c27fbca
LP
373
374 if (l->unicast_scope)
375 dns_cache_flush(&l->unicast_scope->cache);
376
4e945a6f
LP
377 return s;
378}
379
74b2466e
LP
380DnsServer *link_get_dns_server(Link *l) {
381 assert(l);
382
383 if (!l->current_dns_server)
4e945a6f 384 link_set_dns_server(l, l->dns_servers);
74b2466e
LP
385
386 return l->current_dns_server;
387}
388
389void link_next_dns_server(Link *l) {
390 assert(l);
391
74b2466e
LP
392 if (!l->current_dns_server)
393 return;
394
0eac4623
LP
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) {
4e945a6f 398 link_set_dns_server(l, l->current_dns_server->servers_next);
74b2466e
LP
399 return;
400 }
401
4e945a6f 402 link_set_dns_server(l, l->dns_servers);
74b2466e
LP
403}
404
623a4c97 405int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr_union *in_addr) {
74b2466e
LP
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
427LinkAddress *link_address_free(LinkAddress *a) {
428 if (!a)
429 return NULL;
430
623a4c97 431 if (a->link) {
74b2466e
LP
432 LIST_REMOVE(addresses, a->link->addresses, a);
433
623a4c97 434 if (a->llmnr_address_rr) {
623a4c97
LP
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);
623a4c97
LP
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);
623a4c97
LP
446 }
447 }
448
ec2c5e43
LP
449 dns_resource_record_unref(a->llmnr_address_rr);
450 dns_resource_record_unref(a->llmnr_ptr_rr);
451
74b2466e
LP
452 free(a);
453 return NULL;
454}
455
ec2c5e43 456void link_address_add_rrs(LinkAddress *a, bool force_remove) {
623a4c97
LP
457 int r;
458
459 assert(a);
460
ec2c5e43 461 if (a->family == AF_INET) {
623a4c97 462
4e945a6f
LP
463 if (!force_remove &&
464 link_address_relevant(a) &&
465 a->link->llmnr_ipv4_scope &&
19b50b5b 466 a->link->llmnr_support == SUPPORT_YES &&
4e945a6f
LP
467 a->link->manager->llmnr_support == SUPPORT_YES) {
468
78c6a153
LP
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) {
ec2c5e43
LP
472 r = -ENOMEM;
473 goto fail;
474 }
623a4c97 475 }
623a4c97 476
623a4c97 477 if (!a->llmnr_address_rr) {
78c6a153 478 a->llmnr_address_rr = dns_resource_record_new(a->link->manager->llmnr_host_ipv4_key);
ec2c5e43
LP
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;
623a4c97
LP
486 }
487
ec2c5e43 488 if (!a->llmnr_ptr_rr) {
78c6a153 489 r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->llmnr_hostname);
ec2c5e43
LP
490 if (r < 0)
491 goto fail;
623a4c97 492
ec2c5e43
LP
493 a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL;
494 }
623a4c97 495
ec2c5e43 496 r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_address_rr, true);
623a4c97 497 if (r < 0)
da927ba9 498 log_warning_errno(r, "Failed to add A record to LLMNR zone: %m");
623a4c97 499
ec2c5e43 500 r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_ptr_rr, false);
623a4c97 501 if (r < 0)
da927ba9 502 log_warning_errno(r, "Failed to add IPv6 PTR record to LLMNR zone: %m");
623a4c97 503 } else {
ec2c5e43
LP
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 }
623a4c97
LP
515 }
516 }
517
ec2c5e43 518 if (a->family == AF_INET6) {
623a4c97 519
4e945a6f
LP
520 if (!force_remove &&
521 link_address_relevant(a) &&
522 a->link->llmnr_ipv6_scope &&
19b50b5b 523 a->link->llmnr_support == SUPPORT_YES &&
4e945a6f
LP
524 a->link->manager->llmnr_support == SUPPORT_YES) {
525
78c6a153
LP
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) {
ec2c5e43
LP
529 r = -ENOMEM;
530 goto fail;
531 }
623a4c97 532 }
623a4c97 533
623a4c97 534 if (!a->llmnr_address_rr) {
78c6a153 535 a->llmnr_address_rr = dns_resource_record_new(a->link->manager->llmnr_host_ipv6_key);
ec2c5e43
LP
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;
623a4c97
LP
543 }
544
ec2c5e43 545 if (!a->llmnr_ptr_rr) {
78c6a153 546 r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->llmnr_hostname);
ec2c5e43
LP
547 if (r < 0)
548 goto fail;
623a4c97 549
ec2c5e43
LP
550 a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL;
551 }
623a4c97 552
ec2c5e43 553 r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_address_rr, true);
623a4c97 554 if (r < 0)
da927ba9 555 log_warning_errno(r, "Failed to add AAAA record to LLMNR zone: %m");
623a4c97 556
ec2c5e43 557 r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_ptr_rr, false);
623a4c97 558 if (r < 0)
da927ba9 559 log_warning_errno(r, "Failed to add IPv6 PTR record to LLMNR zone: %m");
623a4c97 560 } else {
ec2c5e43
LP
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 }
623a4c97
LP
572 }
573 }
574
575 return;
576
577fail:
da927ba9 578 log_debug_errno(r, "Failed to update address RRs: %m");
623a4c97
LP
579}
580
1c4baffc 581int link_address_update_rtnl(LinkAddress *a, sd_netlink_message *m) {
74b2466e
LP
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
1716f6dc 590 sd_rtnl_message_addr_get_scope(m, &a->scope);
74b2466e 591
1716f6dc 592 link_allocate_scopes(a->link);
ec2c5e43 593 link_add_rrs(a->link, false);
623a4c97 594
74b2466e
LP
595 return 0;
596}
597
598bool link_address_relevant(LinkAddress *a) {
599 assert(a);
600
7b85d72f 601 if (a->flags & (IFA_F_DEPRECATED|IFA_F_TENTATIVE))
74b2466e
LP
602 return false;
603
604 if (IN_SET(a->scope, RT_SCOPE_HOST, RT_SCOPE_NOWHERE))
605 return false;
606
607 return true;
608}