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