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