]> git.ipfire.org Git - thirdparty/systemd.git/blob - 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
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 "missing.h"
27 #include "parse-util.h"
28 #include "resolved-link.h"
29 #include "string-util.h"
30 #include "strv.h"
31
32 int 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
39 r = hashmap_ensure_allocated(&m->links, NULL);
40 if (r < 0)
41 return r;
42
43 l = new0(Link, 1);
44 if (!l)
45 return -ENOMEM;
46
47 l->ifindex = ifindex;
48 l->llmnr_support = SUPPORT_YES;
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
63 Link *link_free(Link *l) {
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
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 }
79
80 dns_scope_free(l->unicast_scope);
81 dns_scope_free(l->llmnr_ipv4_scope);
82 dns_scope_free(l->llmnr_ipv6_scope);
83
84 free(l);
85 return NULL;
86 }
87
88 static void link_allocate_scopes(Link *l) {
89 int r;
90
91 assert(l);
92
93 if (l->dns_servers) {
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)
97 log_warning_errno(r, "Failed to allocate DNS scope: %m");
98 }
99 } else
100 l->unicast_scope = dns_scope_free(l->unicast_scope);
101
102 if (link_relevant(l, AF_INET) &&
103 l->llmnr_support != SUPPORT_NO &&
104 l->manager->llmnr_support != SUPPORT_NO) {
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)
108 log_warning_errno(r, "Failed to allocate LLMNR IPv4 scope: %m");
109 }
110 } else
111 l->llmnr_ipv4_scope = dns_scope_free(l->llmnr_ipv4_scope);
112
113 if (link_relevant(l, AF_INET6) &&
114 l->llmnr_support != SUPPORT_NO &&
115 l->manager->llmnr_support != SUPPORT_NO &&
116 socket_ipv6_is_supported()) {
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)
120 log_warning_errno(r, "Failed to allocate LLMNR IPv6 scope: %m");
121 }
122 } else
123 l->llmnr_ipv6_scope = dns_scope_free(l->llmnr_ipv6_scope);
124 }
125
126 void link_add_rrs(Link *l, bool force_remove) {
127 LinkAddress *a;
128
129 LIST_FOREACH(addresses, a, l->addresses)
130 link_address_add_rrs(a, force_remove);
131 }
132
133 int link_update_rtnl(Link *l, sd_netlink_message *m) {
134 const char *n = NULL;
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
144 sd_netlink_message_read_u32(m, IFLA_MTU, &l->mtu);
145
146 if (sd_netlink_message_read_string(m, IFLA_IFNAME, &n) >= 0) {
147 strncpy(l->name, n, sizeof(l->name)-1);
148 char_array_0(l->name);
149 }
150
151 link_allocate_scopes(l);
152 link_add_rrs(l, false);
153
154 return 0;
155 }
156
157 static int link_update_dns_servers(Link *l) {
158 _cleanup_strv_free_ char **nameservers = NULL;
159 char **nameserver;
160 DnsServer *s, *nx;
161 int r;
162
163 assert(l);
164
165 r = sd_network_link_get_dns(l->ifindex, &nameservers);
166 if (r < 0)
167 goto clear;
168
169 LIST_FOREACH(servers, s, l->dns_servers)
170 s->marked = true;
171
172 STRV_FOREACH(nameserver, nameservers) {
173 union in_addr_union a;
174 int family;
175
176 r = in_addr_from_string_auto(*nameserver, &family, &a);
177 if (r < 0)
178 goto clear;
179
180 s = link_find_dns_server(l, family, &a);
181 if (s)
182 s->marked = false;
183 else {
184 r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, family, &a);
185 if (r < 0)
186 goto clear;
187 }
188 }
189
190 LIST_FOREACH_SAFE(servers, s, nx, l->dns_servers)
191 if (s->marked) {
192 LIST_REMOVE(servers, l->dns_servers, s);
193 dns_server_unref(s);
194 }
195
196 return 0;
197
198 clear:
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 }
205
206 return r;
207 }
208
209 static int link_update_llmnr_support(Link *l) {
210 _cleanup_free_ char *b = NULL;
211 int r;
212
213 assert(l);
214
215 r = sd_network_link_get_llmnr(l->ifindex, &b);
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
233 clear:
234 l->llmnr_support = SUPPORT_YES;
235 return r;
236 }
237
238 static int link_update_domains(Link *l) {
239 int r;
240
241 if (!l->unicast_scope)
242 return 0;
243
244 l->unicast_scope->domains = strv_free(l->unicast_scope->domains);
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
254 int link_update_monitor(Link *l) {
255 assert(l);
256
257 link_update_dns_servers(l);
258 link_update_llmnr_support(l);
259 link_allocate_scopes(l);
260 link_update_domains(l);
261 link_add_rrs(l, false);
262
263 return 0;
264 }
265
266 bool link_relevant(Link *l, int family) {
267 _cleanup_free_ char *state = NULL;
268 LinkAddress *a;
269
270 assert(l);
271
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;
278
279 if ((l->flags & (IFF_UP|IFF_LOWER_UP|IFF_MULTICAST)) != (IFF_UP|IFF_LOWER_UP|IFF_MULTICAST))
280 return false;
281
282 sd_network_link_get_operational_state(l->ifindex, &state);
283 if (state && !STR_IN_SET(state, "unknown", "degraded", "routable"))
284 return false;
285
286 LIST_FOREACH(addresses, a, l->addresses)
287 if (a->family == family && link_address_relevant(a))
288 return true;
289
290 return false;
291 }
292
293 LinkAddress *link_find_address(Link *l, int family, const union in_addr_union *in_addr) {
294 LinkAddress *a;
295
296 assert(l);
297
298 LIST_FOREACH(addresses, a, l->addresses)
299 if (a->family == family && in_addr_equal(family, &a->in_addr, in_addr))
300 return a;
301
302 return NULL;
303 }
304
305 DnsServer* link_find_dns_server(Link *l, int family, const union in_addr_union *in_addr) {
306 DnsServer *s;
307
308 assert(l);
309
310 LIST_FOREACH(servers, s, l->dns_servers)
311 if (s->family == family && in_addr_equal(family, &s->address, in_addr))
312 return s;
313 return NULL;
314 }
315
316 DnsServer* link_set_dns_server(Link *l, DnsServer *s) {
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);
327 }
328
329 l->current_dns_server = s;
330
331 if (l->unicast_scope)
332 dns_cache_flush(&l->unicast_scope->cache);
333
334 return s;
335 }
336
337 DnsServer *link_get_dns_server(Link *l) {
338 assert(l);
339
340 if (!l->current_dns_server)
341 link_set_dns_server(l, l->dns_servers);
342
343 return l->current_dns_server;
344 }
345
346 void link_next_dns_server(Link *l) {
347 assert(l);
348
349 if (!l->current_dns_server)
350 return;
351
352 if (l->current_dns_server->servers_next) {
353 link_set_dns_server(l, l->current_dns_server->servers_next);
354 return;
355 }
356
357 link_set_dns_server(l, l->dns_servers);
358 }
359
360 int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr_union *in_addr) {
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
382 LinkAddress *link_address_free(LinkAddress *a) {
383 if (!a)
384 return NULL;
385
386 if (a->link) {
387 LIST_REMOVE(addresses, a->link->addresses, a);
388
389 if (a->llmnr_address_rr) {
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);
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);
401 }
402 }
403
404 dns_resource_record_unref(a->llmnr_address_rr);
405 dns_resource_record_unref(a->llmnr_ptr_rr);
406
407 free(a);
408 return NULL;
409 }
410
411 void link_address_add_rrs(LinkAddress *a, bool force_remove) {
412 int r;
413
414 assert(a);
415
416 if (a->family == AF_INET) {
417
418 if (!force_remove &&
419 link_address_relevant(a) &&
420 a->link->llmnr_ipv4_scope &&
421 a->link->llmnr_support == SUPPORT_YES &&
422 a->link->manager->llmnr_support == SUPPORT_YES) {
423
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) {
427 r = -ENOMEM;
428 goto fail;
429 }
430 }
431
432 if (!a->llmnr_address_rr) {
433 a->llmnr_address_rr = dns_resource_record_new(a->link->manager->llmnr_host_ipv4_key);
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;
441 }
442
443 if (!a->llmnr_ptr_rr) {
444 r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->llmnr_hostname);
445 if (r < 0)
446 goto fail;
447
448 a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL;
449 }
450
451 r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_address_rr, true);
452 if (r < 0)
453 log_warning_errno(r, "Failed to add A record to LLMNR zone: %m");
454
455 r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_ptr_rr, false);
456 if (r < 0)
457 log_warning_errno(r, "Failed to add IPv6 PTR record to LLMNR zone: %m");
458 } else {
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 }
470 }
471 }
472
473 if (a->family == AF_INET6) {
474
475 if (!force_remove &&
476 link_address_relevant(a) &&
477 a->link->llmnr_ipv6_scope &&
478 a->link->llmnr_support == SUPPORT_YES &&
479 a->link->manager->llmnr_support == SUPPORT_YES) {
480
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) {
484 r = -ENOMEM;
485 goto fail;
486 }
487 }
488
489 if (!a->llmnr_address_rr) {
490 a->llmnr_address_rr = dns_resource_record_new(a->link->manager->llmnr_host_ipv6_key);
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;
498 }
499
500 if (!a->llmnr_ptr_rr) {
501 r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->llmnr_hostname);
502 if (r < 0)
503 goto fail;
504
505 a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL;
506 }
507
508 r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_address_rr, true);
509 if (r < 0)
510 log_warning_errno(r, "Failed to add AAAA record to LLMNR zone: %m");
511
512 r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_ptr_rr, false);
513 if (r < 0)
514 log_warning_errno(r, "Failed to add IPv6 PTR record to LLMNR zone: %m");
515 } else {
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 }
527 }
528 }
529
530 return;
531
532 fail:
533 log_debug_errno(r, "Failed to update address RRs: %m");
534 }
535
536 int link_address_update_rtnl(LinkAddress *a, sd_netlink_message *m) {
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
545 sd_rtnl_message_addr_get_scope(m, &a->scope);
546
547 link_allocate_scopes(a->link);
548 link_add_rrs(a->link, false);
549
550 return 0;
551 }
552
553 bool link_address_relevant(LinkAddress *a) {
554 assert(a);
555
556 if (a->flags & (IFA_F_DEPRECATED|IFA_F_TENTATIVE))
557 return false;
558
559 if (IN_SET(a->scope, RT_SCOPE_HOST, RT_SCOPE_NOWHERE))
560 return false;
561
562 return true;
563 }