]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-link.c
tree-wide: make use of log_error_errno() return value
[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"
74b2466e 25#include "strv.h"
ec2c5e43 26#include "missing.h"
74b2466e
LP
27#include "resolved-link.h"
28
29int link_new(Manager *m, Link **ret, int ifindex) {
30 _cleanup_(link_freep) Link *l = NULL;
31 int r;
32
33 assert(m);
34 assert(ifindex > 0);
35
d5099efc 36 r = hashmap_ensure_allocated(&m->links, NULL);
74b2466e
LP
37 if (r < 0)
38 return r;
39
40 l = new0(Link, 1);
41 if (!l)
42 return -ENOMEM;
43
44 l->ifindex = ifindex;
19b50b5b 45 l->llmnr_support = SUPPORT_YES;
74b2466e
LP
46
47 r = hashmap_put(m->links, INT_TO_PTR(ifindex), l);
48 if (r < 0)
49 return r;
50
51 l->manager = m;
52
53 if (ret)
54 *ret = l;
55 l = NULL;
56
57 return 0;
58}
59
60Link *link_free(Link *l) {
74b2466e
LP
61 if (!l)
62 return NULL;
63
64 while (l->addresses)
65 link_address_free(l->addresses);
66
67 if (l->manager)
68 hashmap_remove(l->manager->links, INT_TO_PTR(l->ifindex));
69
91b14d6f
TG
70 while (l->dns_servers) {
71 DnsServer *s = l->dns_servers;
72
73 LIST_REMOVE(servers, l->dns_servers, s);
74 dns_server_unref(s);
75 }
cab5b059 76
74b2466e 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;
74b2466e 157 DnsServer *s, *nx;
6f4dedb2 158 int r;
74b2466e
LP
159
160 assert(l);
161
d6731e4c 162 r = sd_network_link_get_dns(l->ifindex, &nameservers);
6f4dedb2 163 if (r < 0)
74b2466e 164 goto clear;
74b2466e 165
5cb36f41
LP
166 LIST_FOREACH(servers, s, l->dns_servers)
167 s->marked = true;
168
6f4dedb2
TG
169 STRV_FOREACH(nameserver, nameservers) {
170 union in_addr_union a;
171 int family;
74b2466e 172
6f4dedb2
TG
173 r = in_addr_from_string_auto(*nameserver, &family, &a);
174 if (r < 0)
175 goto clear;
74b2466e 176
6f4dedb2 177 s = link_find_dns_server(l, family, &a);
74b2466e
LP
178 if (s)
179 s->marked = false;
180 else {
4e945a6f 181 r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, family, &a);
74b2466e
LP
182 if (r < 0)
183 goto clear;
184 }
185 }
186
6073b6f2 187 LIST_FOREACH_SAFE(servers, s, nx, l->dns_servers)
91b14d6f
TG
188 if (s->marked) {
189 LIST_REMOVE(servers, l->dns_servers, s);
190 dns_server_unref(s);
191 }
74b2466e
LP
192
193 return 0;
194
195clear:
91b14d6f
TG
196 while (l->dns_servers) {
197 s = l->dns_servers;
198
199 LIST_REMOVE(servers, l->dns_servers, s);
200 dns_server_unref(s);
201 }
74b2466e
LP
202
203 return r;
204}
205
19b50b5b
LP
206static int link_update_llmnr_support(Link *l) {
207 _cleanup_free_ char *b = NULL;
208 int r;
209
210 assert(l);
211
d6731e4c 212 r = sd_network_link_get_llmnr(l->ifindex, &b);
19b50b5b
LP
213 if (r < 0)
214 goto clear;
215
216 r = parse_boolean(b);
217 if (r < 0) {
218 if (streq(b, "resolve"))
219 l->llmnr_support = SUPPORT_RESOLVE;
220 else
221 goto clear;
222
223 } else if (r > 0)
224 l->llmnr_support = SUPPORT_YES;
225 else
226 l->llmnr_support = SUPPORT_NO;
227
228 return 0;
229
230clear:
231 l->llmnr_support = SUPPORT_YES;
232 return r;
233}
234
bda2c408
TG
235static int link_update_domains(Link *l) {
236 int r;
237
238 if (!l->unicast_scope)
239 return 0;
240
241 strv_free(l->unicast_scope->domains);
242 l->unicast_scope->domains = NULL;
243
244 r = sd_network_link_get_domains(l->ifindex,
245 &l->unicast_scope->domains);
246 if (r < 0)
247 return r;
248
249 return 0;
250}
251
74b2466e
LP
252int link_update_monitor(Link *l) {
253 assert(l);
254
6073b6f2 255 link_update_dns_servers(l);
19b50b5b 256 link_update_llmnr_support(l);
1716f6dc 257 link_allocate_scopes(l);
bda2c408 258 link_update_domains(l);
ec2c5e43 259 link_add_rrs(l, false);
74b2466e
LP
260
261 return 0;
262}
263
0dd25fb9 264bool link_relevant(Link *l, int family) {
1716f6dc 265 _cleanup_free_ char *state = NULL;
74b2466e
LP
266 LinkAddress *a;
267
268 assert(l);
269
ec2c5e43
LP
270 /* A link is relevant if it isn't a loopback or pointopoint
271 * device, has a link beat, can do multicast and has at least
272 * one relevant IP address */
273
274 if (l->flags & (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_DORMANT))
275 return false;
74b2466e 276
ec2c5e43 277 if ((l->flags & (IFF_UP|IFF_LOWER_UP|IFF_MULTICAST)) != (IFF_UP|IFF_LOWER_UP|IFF_MULTICAST))
74b2466e
LP
278 return false;
279
d6731e4c 280 sd_network_link_get_operational_state(l->ifindex, &state);
1716f6dc 281 if (state && !STR_IN_SET(state, "unknown", "degraded", "routable"))
74b2466e
LP
282 return false;
283
284 LIST_FOREACH(addresses, a, l->addresses)
1716f6dc 285 if (a->family == family && link_address_relevant(a))
74b2466e
LP
286 return true;
287
288 return false;
289}
290
623a4c97 291LinkAddress *link_find_address(Link *l, int family, const union in_addr_union *in_addr) {
74b2466e
LP
292 LinkAddress *a;
293
294 assert(l);
295
1716f6dc
LP
296 LIST_FOREACH(addresses, a, l->addresses)
297 if (a->family == family && in_addr_equal(family, &a->in_addr, in_addr))
74b2466e 298 return a;
74b2466e
LP
299
300 return NULL;
301}
302
623a4c97 303DnsServer* link_find_dns_server(Link *l, int family, const union in_addr_union *in_addr) {
6073b6f2 304 DnsServer *s;
74b2466e
LP
305
306 assert(l);
307
6073b6f2 308 LIST_FOREACH(servers, s, l->dns_servers)
1716f6dc 309 if (s->family == family && in_addr_equal(family, &s->address, in_addr))
74b2466e 310 return s;
74b2466e
LP
311 return NULL;
312}
313
2c27fbca 314DnsServer* link_set_dns_server(Link *l, DnsServer *s) {
4e945a6f
LP
315 assert(l);
316
317 if (l->current_dns_server == s)
318 return s;
319
320 if (s) {
321 _cleanup_free_ char *ip = NULL;
322
323 in_addr_to_string(s->family, &s->address, &ip);
324 log_info("Switching to DNS server %s for interface %s.", strna(ip), l->name);
2c27fbca 325 }
4e945a6f
LP
326
327 l->current_dns_server = s;
2c27fbca
LP
328
329 if (l->unicast_scope)
330 dns_cache_flush(&l->unicast_scope->cache);
331
4e945a6f
LP
332 return s;
333}
334
74b2466e
LP
335DnsServer *link_get_dns_server(Link *l) {
336 assert(l);
337
338 if (!l->current_dns_server)
4e945a6f 339 link_set_dns_server(l, l->dns_servers);
74b2466e
LP
340
341 return l->current_dns_server;
342}
343
344void link_next_dns_server(Link *l) {
345 assert(l);
346
74b2466e
LP
347 if (!l->current_dns_server)
348 return;
349
350 if (l->current_dns_server->servers_next) {
4e945a6f 351 link_set_dns_server(l, l->current_dns_server->servers_next);
74b2466e
LP
352 return;
353 }
354
4e945a6f 355 link_set_dns_server(l, l->dns_servers);
74b2466e
LP
356}
357
623a4c97 358int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr_union *in_addr) {
74b2466e
LP
359 LinkAddress *a;
360
361 assert(l);
362 assert(in_addr);
363
364 a = new0(LinkAddress, 1);
365 if (!a)
366 return -ENOMEM;
367
368 a->family = family;
369 a->in_addr = *in_addr;
370
371 a->link = l;
372 LIST_PREPEND(addresses, l->addresses, a);
373
374 if (ret)
375 *ret = a;
376
377 return 0;
378}
379
380LinkAddress *link_address_free(LinkAddress *a) {
381 if (!a)
382 return NULL;
383
623a4c97 384 if (a->link) {
74b2466e
LP
385 LIST_REMOVE(addresses, a->link->addresses, a);
386
623a4c97 387 if (a->llmnr_address_rr) {
623a4c97
LP
388 if (a->family == AF_INET && a->link->llmnr_ipv4_scope)
389 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr);
390 else if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope)
391 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr);
623a4c97
LP
392 }
393
394 if (a->llmnr_ptr_rr) {
395 if (a->family == AF_INET && a->link->llmnr_ipv4_scope)
396 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_ptr_rr);
397 else if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope)
398 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr);
623a4c97
LP
399 }
400 }
401
ec2c5e43
LP
402 dns_resource_record_unref(a->llmnr_address_rr);
403 dns_resource_record_unref(a->llmnr_ptr_rr);
404
74b2466e
LP
405 free(a);
406 return NULL;
407}
408
ec2c5e43 409void link_address_add_rrs(LinkAddress *a, bool force_remove) {
623a4c97
LP
410 int r;
411
412 assert(a);
413
ec2c5e43 414 if (a->family == AF_INET) {
623a4c97 415
4e945a6f
LP
416 if (!force_remove &&
417 link_address_relevant(a) &&
418 a->link->llmnr_ipv4_scope &&
19b50b5b 419 a->link->llmnr_support == SUPPORT_YES &&
4e945a6f
LP
420 a->link->manager->llmnr_support == SUPPORT_YES) {
421
78c6a153
LP
422 if (!a->link->manager->llmnr_host_ipv4_key) {
423 a->link->manager->llmnr_host_ipv4_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, a->link->manager->llmnr_hostname);
424 if (!a->link->manager->llmnr_host_ipv4_key) {
ec2c5e43
LP
425 r = -ENOMEM;
426 goto fail;
427 }
623a4c97 428 }
623a4c97 429
623a4c97 430 if (!a->llmnr_address_rr) {
78c6a153 431 a->llmnr_address_rr = dns_resource_record_new(a->link->manager->llmnr_host_ipv4_key);
ec2c5e43
LP
432 if (!a->llmnr_address_rr) {
433 r = -ENOMEM;
434 goto fail;
435 }
436
437 a->llmnr_address_rr->a.in_addr = a->in_addr.in;
438 a->llmnr_address_rr->ttl = LLMNR_DEFAULT_TTL;
623a4c97
LP
439 }
440
ec2c5e43 441 if (!a->llmnr_ptr_rr) {
78c6a153 442 r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->llmnr_hostname);
ec2c5e43
LP
443 if (r < 0)
444 goto fail;
623a4c97 445
ec2c5e43
LP
446 a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL;
447 }
623a4c97 448
ec2c5e43 449 r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_address_rr, true);
623a4c97 450 if (r < 0)
da927ba9 451 log_warning_errno(r, "Failed to add A record to LLMNR zone: %m");
623a4c97 452
ec2c5e43 453 r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_ptr_rr, false);
623a4c97 454 if (r < 0)
da927ba9 455 log_warning_errno(r, "Failed to add IPv6 PTR record to LLMNR zone: %m");
623a4c97 456 } else {
ec2c5e43
LP
457 if (a->llmnr_address_rr) {
458 if (a->link->llmnr_ipv4_scope)
459 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr);
460 a->llmnr_address_rr = dns_resource_record_unref(a->llmnr_address_rr);
461 }
462
463 if (a->llmnr_ptr_rr) {
464 if (a->link->llmnr_ipv4_scope)
465 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_ptr_rr);
466 a->llmnr_ptr_rr = dns_resource_record_unref(a->llmnr_ptr_rr);
467 }
623a4c97
LP
468 }
469 }
470
ec2c5e43 471 if (a->family == AF_INET6) {
623a4c97 472
4e945a6f
LP
473 if (!force_remove &&
474 link_address_relevant(a) &&
475 a->link->llmnr_ipv6_scope &&
19b50b5b 476 a->link->llmnr_support == SUPPORT_YES &&
4e945a6f
LP
477 a->link->manager->llmnr_support == SUPPORT_YES) {
478
78c6a153
LP
479 if (!a->link->manager->llmnr_host_ipv6_key) {
480 a->link->manager->llmnr_host_ipv6_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, a->link->manager->llmnr_hostname);
481 if (!a->link->manager->llmnr_host_ipv6_key) {
ec2c5e43
LP
482 r = -ENOMEM;
483 goto fail;
484 }
623a4c97 485 }
623a4c97 486
623a4c97 487 if (!a->llmnr_address_rr) {
78c6a153 488 a->llmnr_address_rr = dns_resource_record_new(a->link->manager->llmnr_host_ipv6_key);
ec2c5e43
LP
489 if (!a->llmnr_address_rr) {
490 r = -ENOMEM;
491 goto fail;
492 }
493
494 a->llmnr_address_rr->aaaa.in6_addr = a->in_addr.in6;
495 a->llmnr_address_rr->ttl = LLMNR_DEFAULT_TTL;
623a4c97
LP
496 }
497
ec2c5e43 498 if (!a->llmnr_ptr_rr) {
78c6a153 499 r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->llmnr_hostname);
ec2c5e43
LP
500 if (r < 0)
501 goto fail;
623a4c97 502
ec2c5e43
LP
503 a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL;
504 }
623a4c97 505
ec2c5e43 506 r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_address_rr, true);
623a4c97 507 if (r < 0)
da927ba9 508 log_warning_errno(r, "Failed to add AAAA record to LLMNR zone: %m");
623a4c97 509
ec2c5e43 510 r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_ptr_rr, false);
623a4c97 511 if (r < 0)
da927ba9 512 log_warning_errno(r, "Failed to add IPv6 PTR record to LLMNR zone: %m");
623a4c97 513 } else {
ec2c5e43
LP
514 if (a->llmnr_address_rr) {
515 if (a->link->llmnr_ipv6_scope)
516 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr);
517 a->llmnr_address_rr = dns_resource_record_unref(a->llmnr_address_rr);
518 }
519
520 if (a->llmnr_ptr_rr) {
521 if (a->link->llmnr_ipv6_scope)
522 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr);
523 a->llmnr_ptr_rr = dns_resource_record_unref(a->llmnr_ptr_rr);
524 }
623a4c97
LP
525 }
526 }
527
528 return;
529
530fail:
da927ba9 531 log_debug_errno(r, "Failed to update address RRs: %m");
623a4c97
LP
532}
533
1c4baffc 534int link_address_update_rtnl(LinkAddress *a, sd_netlink_message *m) {
74b2466e
LP
535 int r;
536 assert(a);
537 assert(m);
538
539 r = sd_rtnl_message_addr_get_flags(m, &a->flags);
540 if (r < 0)
541 return r;
542
1716f6dc 543 sd_rtnl_message_addr_get_scope(m, &a->scope);
74b2466e 544
1716f6dc 545 link_allocate_scopes(a->link);
ec2c5e43 546 link_add_rrs(a->link, false);
623a4c97 547
74b2466e
LP
548 return 0;
549}
550
551bool link_address_relevant(LinkAddress *a) {
552 assert(a);
553
7b85d72f 554 if (a->flags & (IFA_F_DEPRECATED|IFA_F_TENTATIVE))
74b2466e
LP
555 return false;
556
557 if (IN_SET(a->scope, RT_SCOPE_HOST, RT_SCOPE_NOWHERE))
558 return false;
559
560 return true;
561}